I'm looking for a utility that can quickly escape a string of characters. This task is so useful but i can't found it.
Lets say for an example:
hisrmline 'h | g -E "^ [0-9]* exit$"'
If i want to manually escape it, it can be done:
'hisrmline '\''h | g -E "^ [0-9]* exit$"'\'''
But it's time consuming and not efficient. So i found 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]$
The output is wrong because hisrmlineh was concate together, so i'm narrow down the strings:
[xiaobai@xiaobai note]$ printf "%q" hisrmline 'h'
hisrmlineh[xiaobai@xiaobai note]$
[xiaobai@xiaobai note]$
What i desired was hisrmline\ \'h\'
This is especially useful on 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$"''
And grep -F wouldn't make my life easier when deal with nested single quote, i still have to manually escape the single quote '\'':
[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$"'"'"'
Is there any easier way or any existing utility to escape list of any characters string ?
If you quote the command line properly then printf should work e.g.:
printf "%q\n" "hisrmline 'h'"
hisrmline\ \'h\'
Or:
printf "%q\n" "hisrmline 'h | g -E \"^ [0-9]* exit$\"'"
hisrmline\ \'h\ \|\ g\ -E\ \"\^\ \[0-9\]\*\ \ exit\$\"\'
EDIT: You're probably looking for this kind of escaping:
IFS= read -r str<<"EOF"
hisrmline 'h | g -E "^ [0-9]* exit$"'
EOF
printf "%q\n" "$str"
hisrmline\ \'h\ \|\ g\ -E\ \"\^\ \[0-9\]\*\ \ exit\$\"\'
[UPDATE by @林果皞]
To whom might be interested, EOF have to quoted to prevent expansion, as noted by @bize:
EOF without quoted:
[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" quoted:
[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]$
Correct behaviour only when supplied from the output of quoted "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 is aliased to export HISTTIMEFORMAT=""; history
*g is aliased togrep -a --color=auto
Direct use of $ h|g -F "$str" is working too.
When deal with unicode, i have to assign LC_ALL= to empty(i.e LC_ALL="en_US.utf8") before query(history, ls..etc) the source string. Then i have to switch it to LC_ALL=C to make printf %q working correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With