bash - 快速转义任何字符串

时间:2015-04-05 12:19:16

标签: bash shell grep escaping printf

我正在寻找能够快速逃脱一串字符的实用程序。这项任务非常有用,但我找不到它。

让我们举一个例子:

hisrmline 'h | g -E "^ [0-9]*  exit$"'

如果我想手动转义它,可以这样做:

'hisrmline '\''h | g -E "^ [0-9]*  exit$"'\'''

但这耗费时间并且效率不高。所以我发现 printf%q

[xiaobai@xiaobai note]$ printf "%q" hisrmline 'h | g -E "^ [0-9]*  exit$"'
hisrmlineh\ \|\ g\ -E\ \"\^\ \[0-9\]\*\ \ exit\$\"[xiaobai@xiaobai note]$ 
[xiaobai@xiaobai note]$ 

输出错误,因为hisrmlineh连在一起,所以我缩小了字符串:

[xiaobai@xiaobai note]$ printf "%q" hisrmline 'h'
hisrmlineh[xiaobai@xiaobai note]$ 
[xiaobai@xiaobai note]$ 

我想要的是 hisrmline \ \'h'

这对grep:

特别有用
[xiaobai@xiaobai note]$ HISTTIMEFORMAT=""; history|grep -a --color=auto hisrmline\ \'h
 7856  hisrmline 'hisrmline'
 7857  hisrmline 'hisrmline'
 7882  hisrmline 'h | g -E "^ [0-9]*  exit[ ]*$"'
 7883  hisrmline 'h | g -E "^ [0-9]*  exit[ ]*$"'
 7884  hisrmline 'h | g -E "'
 7885  hisrmline 'h | g '
 7886  hisrmline 'h | g'
 7887  hisrmline 'h |'
 7890  hisrmline 'h | g -E "^ [0-9]*  exit$"'
 7891  hisrmline 'h | g -E "^ [0-9]*  exit$"'
 7905  h|g 'hisrmline 'h | g -E "^ [0-9]*  exit$"''

grep -F在处理嵌套单引号时不会让我的生活更轻松,我仍然需要手动转义单引号'\'':

[xiaobai@xiaobai note]$ HISTTIMEFORMAT=""; history|grep -a --color=auto -F  '[0-9]*  exit$"'\'''
 7889  h|g -aF 'h | g -E "^ [0-9]*  exit$"'
 7890  hisrmline 'h | g -E "^ [0-9]*  exit$"'
 7891  hisrmline 'h | g -E "^ [0-9]*  exit$"'
 7905  h|g 'hisrmline 'h | g -E "^ [0-9]*  exit$"''
 7911  h|g 'hisrmline 'h | g -E "^ [0-9]*  exit$"''
 7912  h|g 'hisrmline '"'"'h | g -E "^ [0-9]*  exit$"'"'"'

是否有更简单的方法或任何现有的实用程序来转义任何字符串的列表?

3 个答案:

答案 0 :(得分:1)

如果您正确引用命令行,那么printf应该可以正常工作:

printf "%q\n" "hisrmline 'h'"
hisrmline\ \'h\'

或者:

printf "%q\n" "hisrmline 'h | g -E \"^ [0-9]*  exit$\"'"
hisrmline\ \'h\ \|\ g\ -E\ \"\^\ \[0-9\]\*\ \ exit\$\"\'

编辑:您可能正在寻找这种逃避行为:

IFS= read -r str<<"EOF"
hisrmline 'h | g -E "^ [0-9]*  exit$"'
EOF

printf "%q\n" "$str"
hisrmline\ \'h\ \|\ g\ -E\ \"\^\ \[0-9\]\*\ \ exit\$\"\'

[@林果UP更新]

对于可能感兴趣的人,EOF必须引用以防止扩张,如@bize所述:

没有引用的EOF:

[xiaobai@xiaobai Downloads]$ IFS= read -r str<<EOF
> target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
> EOF
[xiaobai@xiaobai Downloads]$ printf "%q\n" "$str"
target=\'h\ \|\ g\ -E\ -i\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\[\ \]\"\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\$\"\'\;\ history\|grep\ -aF\ \"h\ \|\ g\ -E\ -i\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\[\ \]\"\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\$\"\"\;\ echo\ 73
[xiaobai@xiaobai Downloads]$ 
引用“EOF”:

[xiaobai@xiaobai Downloads]$ IFS= read -r str<<"EOF"
> target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
> EOF
[xiaobai@xiaobai Downloads]$ printf "%q\n" "$str"
target=\'h\ \|\ g\ -E\ -i\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\[\ \]\"\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\$\"\'\;\ history\|grep\ -aF\ \"\$target\"\;\ echo\ \$\{#target\}
[xiaobai@xiaobai Downloads]$ 

仅在引用的“EOF”输出中提供正确的行为:

[xiaobai@xiaobai Downloads]$ h|g -F target=\'h\ \|\ g\ -E\ -i\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\[\ \]\"\ -e\ \"\^\[\ \]+\[0-9\]+\ \ .\*\[\|\&\;\ \]+g\$\"\'\;\ history\|grep\ -aF\ \"\$target\"\;\ echo\ \$\{#target\}
 7721  target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
 7725  target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
 7726  atarget='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
 8297  target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}
 8320  target='h | g -E -i -e "^[ ]+[0-9]+  .*[|&; ]+g[ ]" -e "^[ ]+[0-9]+  .*[|&; ]+g$"'; history|grep -aF "$target"; echo ${#target}

* h别名为export HISTTIMEFORMAT=""; history *g is aliased to grep -a --color = auto

直接使用$ h|g -F "$str"也有效。

当处理unicode时,我必须在查询(history,ls..etc)源字符串之前将LC_ALL =指定为空(即LC_ALL =“en_US.utf8”)。然后我必须将其切换到LC_ALL = C以使printf%q正常工作。

答案 1 :(得分:1)

<强>更新

在评论中,您告诉您复制历史记录中的行并希望将它们重新插入shell命令中。在bash中,history expansion可以访问部分历史记录或对其进行修改。可能这已经是你想要的了。

否则你可以创建一个小命令来显示转义的历史记录:

IFS=$'\n' history | while read line ; do printf "%q\n" "$line"; done

您可以复制该输出中的行并将其插入shell字符串中。如果$HISTSIZE很大,您可以另外将其管道化。

如果您需要更频繁地使用此命令,可以从中创建脚本文件或在.bashrc

中创建一个函数

原始答案

假设想要使用'作为字符串分隔符,可以使用以下bash表达式:

string="hisrmline 'h | g -E \"^ [0-9]*  exit$\"'"
echo "${string//\'/\\\'}"

现在你可以在bash中使用字符串了。如果要在grep或其他使用正则表达式的程序中使用它,则需要转义其他字符。但是grep支持选项-F。如果你传递它,模式将被处理为固定字符串,而不是正则表达式。

答案 2 :(得分:1)

吻吻:

printf "%q" "$(cat <<"_up_to_here_"
hisrmline 'h | g -E "^ [0-9]*  exit$"'
_up_to_here_
)"

"_up_to_here_"_up_to_here_之间的任何内容都将被正确引用。

请注意:   引用第一个_up_to_here_以防止在下一行或行(S)中扩展任何$变量。

评论:使用cat旨在使命令保持简单,任何正确转换为read的尝试都需要广泛的知识:不是KISS方法。