bash - 快速转义任何字符串

bash - quickly escape any characters string

我正在寻找一个可以快速转义字符串的实用程序。这个任务太有用了,我找不到了

举个例子:

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$"'"'"'

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

如果您正确引用命令行,那么 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$\"\'

[由@林果曜更新]

对于可能感兴趣的人,必须引用 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 togrep -a --color=auto

的别名

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

处理 unicode 时,我必须在查询(历史、ls..等)源字符串之前将 LC_ALL= 分配给空(即 LC_ALL="en_US.utf8")。然后我必须将它切换到 LC_ALL=C 以使 printf %q 正常工作。

更新:

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

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

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

您可以从该输出复制行并将它们插入到 shell 字符串中。如果您的 $HISTSIZE 很大,您还可以通过管道将其设置为 less。

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

中创建一个函数

原答案

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

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

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

亲吻方法:

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) 中扩展任何 $variable。

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