首页 文章

转义字符串以获取sed替换模式

提问于
浏览
261

在我的bash脚本中,我有一个外部(从用户接收)字符串,我应该在sed模式中使用它 .

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

如何逃避 $REPLACE 字符串,以便 sed 可以安全地接受它作为字面替换?

NOTE: KEYWORD 是一个没有匹配等的哑子串 . 它不是由用户提供的 .

14 回答

  • -2

    如果您只是想在sed命令中替换Variable值,那么只需删除示例:

    sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test
    
  • 29

    更简单的方法是简单地构建字符串并将其用作 sed 的参数

    rpstring="s/KEYWORD/$REPLACE/g"
    sed -i $rpstring  test.txt
    
  • 9

    Warning :这不考虑换行符 . 有关更深入的答案,请参阅this SO-question . (谢谢,Ed Morton和Niklas Peter)

    请注意,逃避一切都是一个坏主意 . Sed需要转义许多字符以获得它们的特殊含义 . 例如,如果您在替换字符串中转义一个数字,它将转入反向引用 .

    正如Ben Blank所说,在替换字符串中只需要转义三个字符(转义自身,正斜杠表示结束语句和&替换全部):

    sed -e 's/[\/&]/\\&/g'
    

    如果您需要转义 KEYWORD 字符串,则需要以下内容:

    sed -e 's/[]\/$*.^[]/\\&/g'
    

    请记住,如果您使用 / 以外的字符作为分隔符,则需要使用您正在使用的字符替换上面表达式中的斜杠 . 请参阅PeterJCLaw的评论以获得解释 .

    编辑:由于以前没有考虑到一些角落情况,上面的命令已经改变了好几次 . 检查编辑历史记录以获取详细信

  • 14

    sed命令允许您使用其他字符而不是 / 作为分隔符:

    sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'
    

    双引号不是问题 .

  • 3

    在replace子句中专门处理的唯一三个文字字符是 / (关闭子句), \ (转义字符,反向引用,&c . )和 & (包括替换中的匹配) . 因此,您需要做的就是逃避这三个字符:

    sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
    

    例:

    $ export REPLACE="'\"|\\/><&!"
    $ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
    foo'"|\/><&!bar
    
  • 39

    基于Pianosaurus的正则表达式,我创建了一个bash函数,它可以逃避关键字和替换 .

    function sedeasy {
      sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
    }
    

    这是你如何使用它:

    sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf
    
  • 0

    回复有点迟了......但有一种更简单的方法可以做到这一点 . 只需更改分隔符(即分隔字段的字符) . 所以,而不是 s/foo/bar/ 你写 s|bar|foo .

    而且,这是简单的方法:

    sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'
    

    结果输出没有那个令人讨厌的DEFINER子句 .

  • -1

    事实证明你're asking the wrong question. I also asked the wrong question. The reason it'错误是第一句话的开头:“在我的 bash 脚本......” .

    我有同样的问题并犯了同样的错误 . 如果你需要使用sed来进行字符串替换(并且使用bash中内置的替换功能会更加清晰) .

    而不是像,例如:

    function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
    INPUT='some long string with KEYWORD that need replacing KEYWORD.'
    A="$(escape-all-funny-characters 'KEYWORD')"
    B="$(escape-all-funny-characters '<funny characters here>')"
    OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"
    

    你可以专门使用bash功能:

    INPUT='some long string with KEYWORD that need replacing KEYWORD.'
    A='KEYWORD'
    B='<funny characters here>'
    OUTPUT="${INPUT//"$A"/"$B"}"
    
  • 0

    使用awk - 它更干净:

    $ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
    http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare
    
  • 1

    这是我刚才使用的AWK的一个例子 . 这是一款打印新AWKS的AWK . AWK和SED相似,它可能是一个很好的模板 .

    ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds
    

    它看起来过分,但不知何故,引号的组合可以保持'打印为文字 . 然后,如果我没记错的话,vaiables就会被这样的引号所包围:“$ 1” . 尝试一下,让我知道它如何与SED一起使用 .

  • 65

    我对sedeasy函数有所改进,它将突破像tab这样的特殊字符 .

    function sedeasy_improved {
        sed -i "s/$(
            echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' 
                | sed -e 's:\t:\\t:g'
        )/$(
            echo "$2" | sed -e 's/[\/&]/\\&/g' 
                | sed -e 's:\t:\\t:g'
        )/g" "$3"
    }
    

    那么,有什么不同? $1$2 用引号括起来以避免shell扩展并保留制表符或双空格 .

    额外的管道 | sed -e 's:\t:\\t:g' (我喜欢 : 作为标记),它转换 \t 中的标签 .

  • 1

    不要忘记“和”周围的shell限制所带来的所有乐趣

    所以(以ksh为单位)

    Var=">New version of \"content' here <"
    printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar
    
    echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"
    
  • -1

    如果情况恰好是您生成随机密码以传递给 sed 替换模式,那么您选择要小心随机字符串中的哪组字符 . 如果您选择通过将值编码为base64而生成的密码,则只有在base64中可能存在的字符,并且在 sed 替换模式中也是一个特殊字符 . 该字符是"/",可以从您生成的密码中轻松删除:

    # password 32 characters log, minus any copies of the "/" character.
    pass=`openssl rand -base64 32 | sed -e 's/\///g'`;
    
  • 229

    只是逃避REPLACE varible中的所有内容:

    echo $REPLACE | awk '{gsub(".", "\\\\&");print}'
    

相关问题