【问题标题】:Bash sed: Replace all special characters (Invalid range end)Bash sed:替换所有特殊字符(无效范围结束)
【发布时间】:2017-03-04 21:51:28
【问题描述】:

最终目标是替换文件中包含密码(可以包含任何字符)的行:

...
passw=old#$#$@$#@%{}[]
...

我有带替换的字符串变量: (暂时不要介意双引号)

$old="passw=old#$#$@$#@%{}[]"

$new="passw=new#$#$@$#@%{}[]"

如果我使用简单的sed -i "s#$old#$new#g" file.txt",它显然不能正常工作,因为特殊字符会破坏语法。

为了使它工作,我需要使用\ 转义所有特殊字符,例如:\[

我尝试使用这样的东西:

esc=$("$old" | sed 's/[][`~!@#$%^&*()-_=+{}\|;:",<.>/?'"'"']/\\&/g'

但是它给了我以下错误:

sed: -e expression #1, char42: Invalid range end

任何帮助将不胜感激!

【问题讨论】:

  • 您在设置esc时忘记在搜索文本中转义/
  • 也许有某种标记,所以sed 会忽略变量中的特殊字符?

标签: bash sed


【解决方案1】:

awk 来救援!

请注意,替换是基于正则表达式的,对于awk 中的搜索字符串以及sed 中的搜索和替换字符串,需要转义特殊字符。这需要预处理字符串。或者,如果您输入的文件是结构化的,您可以尝试字符串相等性检查

例如,

$ awk -v old='old#$#$@$#@%{}[]'    \
      -v new='[]{}new#$#$@$#@%'    \
      -v key='passw=' '"^"key{line=$0; 
                              sub(key,"",line); 
                              if(line==old) $0=key new}1' file 

在输入文件上运行

passw=old#$#$@$#@%{}[]
some other line with old#$#$@$#@%{}[]

会生成

pasw=[]{}new#$#$@$#@%
some other line with old#$#$@$#@%{}[]

您还可以对完整的key=value 匹配进行相等检查,如下所示

$ awk -v old='old#$#$@$#@%{}[]' \
      -v new='[]{}new#$#$@$#@%' \
      -v key='passw=' '$0==key old{$0=key new}1' file

或通过搜索和替换包含key=前缀的字符串进一步简化

awk -v old='passw=old#$#$@$#@%{}[]' \
    -v new='passw=[]{}new#$#$@$#@%' \
          '$0==old{$0=new}1' file

这些假设您的密码记录在单独的行上。如果这不是一个有效的假设,您可以将 RS 和 ORS 设置为 ' ' 并删除输出文件的最后一个字符(添加的最后一个 ORS)。另一种解决方案可能是循环遍历字段(例如for(i=1;i&lt;=NF;i++) if($i==old) $i=new...

【讨论】:

  • 感谢您的回复。我对 bash 很陌生,但是我看到这只会替换变量中的文本。我需要替换文件中的部分文本。顺便说一句,密码的“旧”和“新”部分不是静态的。
  • 我试过你的解决方案,但报错"awk: cmd. line:1: fatal: Invalid content of \{\}: /passw=old#$#$@$#@%{}[]/"
  • / 是从哪里来的?
  • 不知道...你测试了吗?如果它在你的测试中运行正常,可能是我做错了什么。
  • 但又是奇怪的。看看这里,看看我得到了什么:pastebin.com/GEMM3mHz
【解决方案2】:

如果没有适当的转义,您将无法实现它。我尝试了 sed、perl 等,但它们都 break ,尤其是在字符 {} 和 [] 中。

您可以应用 sed 一次转义所有非常用字符。
在下面的示例中,我转义了所有不是文本(a-z 和 A-Z)且不是数字(0-9)的内容

$ cat file14
passw=old#$#$@$#@%{}[]

$ old='passw=old#$#$@$#@%{}[]' && echo "$old"  #mind the single quotes.
passw=old#$#$@$#@%{}[]

$ new='passw=new$#$#$$0-.?!@#$%^&*()_+@$#@%[]{}' && echo "$new"
passw=new$#$#$$0-.?!@#$%^&*()_+@$#@%[]{}

$ old=$(sed 's/[^a-zA-Z0-9]/\\&/g' <<<"$old") && echo "$old"
passw\=old\#\$\#\$\@\$\#\@\%\{\}\[\]

$ new=$(sed 's/[^a-zA-Z0-9]/\\&/g' <<<"$new") && echo "$new"
passw\=new\$\#\$\#\$\$0\-\.\?\!\@\#\$\%\^\&\*\(\)\_\+\@\$\#\@\%\[\]\{\}

$ sed -r "s/$old/$new/" file14
passw=new$#$#$$0-.?!@#$%^&*()_+@$#@%[]{}

【讨论】:

  • 我发现这是实现我目标的最清晰方法。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-14
  • 2019-09-07
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 2012-11-06
相关资源
最近更新 更多