在Bash中,是否有任何需要转义的字符的综合列表?是否可以使用sed
来检查?
特别是,我想知道是否需要转义%
。我尝试了
echo "h%h" | sed 's/%/i/g'
并且正常工作,没有转义 %
。这是否意味着不需要转义 %
?这是检查必要性的好方法吗?
更一般地说,在 shell
和 bash
中需要转义的字符是相同的吗?
在Bash中,是否有任何需要转义的字符的综合列表?是否可以使用sed
来检查?
特别是,我想知道是否需要转义%
。我尝试了
echo "h%h" | sed 's/%/i/g'
并且正常工作,没有转义 %
。这是否意味着不需要转义 %
?这是检查必要性的好方法吗?
更一般地说,在 shell
和 bash
中需要转义的字符是相同的吗?
有两个简单而安全的规则,不仅适用于sh
,还适用于bash
。
这适用于除单引号本身之外的所有字符。要转义单引号,请在其前面关闭引用,插入单引号,然后重新打开引用。
'I'\''m a s@fe $tring which ends in newline
'
sed命令:sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"
这适用于除换行符外的所有字符。对于换行符,请使用单引号或双引号。空字符串必须仍然被处理-替换为""
\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"
sed命令: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
.
有一组易于安全使用而且不必转义的字符,如[a-zA-Z0-9,._+:@%/-]
,可以使代码更易读。
I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"
sed命令:LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
。
请注意,在sed程序中,无法确定输入的最后一行是否以换行符结束(除非为空)。这就是为什么上面两个sed命令都假设它没有。您可以手动添加带引号的换行符。
请注意,shell变量仅针对POSIX意义上的文本进行定义。处理二进制数据未被定义。对于有影响的实现,二进制可以工作,但要注意NUL字节(因为变量使用C字符串实现,并且旨在用作C字符串,即程序参数),但应切换到“二进制”区域设置,例如latin1。
(您可以通过阅读sh
的POSIX规范轻松验证规则。对于bash,请查看由@AustinPhillips链接的参考手册)。
${var@Q}
在bash下,您可以使用参数扩展(Parameter Expansion)中的@
命令来存储变量内容以进行参数转换(Parameter transformation):
${parameter@operator}
Parameter transformation. The expansion is either a transforma‐
tion of the value of parameter or information about parameter
itself, depending on the value of operator. Each operator is a
single letter:
Q The expansion is a string that is the value of parameter
quoted in a format that can be reused as input.
...
A The expansion is a string in the form of an assignment
statement or declare command that, if evaluated, will
recreate parameter with its attributes and value.
示例:
$ var=$'Hello\nGood world.\n'
$ echo "$var"
Hello
Good world.
$ echo "${var@Q}"
$'Hello\nGood world.\n'
$ echo "${var@A}"
var=$'Hello\nGood world.\n'
有一个特殊的printf
格式指令(%q
)是为了这种请求而建立的:
printf [-v var] format [arguments]
%q causes printf to output the corresponding argument in a format that can be reused as shell input.
read foo
Hello world
printf "%q\n" "$foo"
Hello\ world
printf "%q\n" $'Hello world!\n'
$'Hello world!\n'
这也可以通过变量来使用:
printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'
请注意,从128到255的所有字节都必须进行转义。
for i in {0..127} ;do
printf -v var \\%o $i
printf -v var $var
printf -v res "%q" "$var"
esc=E
[ "$var" = "$res" ] && esc=-
printf "%02X %s %-7s\n" $i $esc "$res"
done |
column
这必须呈现出类似于:
00 E '' 1A E $'\032' 34 - 4 4E - N 68 - h
01 E $'\001' 1B E $'\E' 35 - 5 4F - O 69 - i
02 E $'\002' 1C E $'\034' 36 - 6 50 - P 6A - j
03 E $'\003' 1D E $'\035' 37 - 7 51 - Q 6B - k
04 E $'\004' 1E E $'\036' 38 - 8 52 - R 6C - l
05 E $'\005' 1F E $'\037' 39 - 9 53 - S 6D - m
06 E $'\006' 20 E \ 3A - : 54 - T 6E - n
07 E $'\a' 21 E \! 3B E \; 55 - U 6F - o
08 E $'\b' 22 E \" 3C E \< 56 - V 70 - p
09 E $'\t' 23 E \# 3D - = 57 - W 71 - q
0A E $'\n' 24 E \$ 3E E \> 58 - X 72 - r
0B E $'\v' 25 - % 3F E \? 59 - Y 73 - s
0C E $'\f' 26 E \& 40 - @ 5A - Z 74 - t
0D E $'\r' 27 E \' 41 - A 5B E \[ 75 - u
0E E $'\016' 28 E \( 42 - B 5C E \\ 76 - v
0F E $'\017' 29 E \) 43 - C 5D E \] 77 - w
10 E $'\020' 2A E \* 44 - D 5E E \^ 78 - x
11 E $'\021' 2B - + 45 - E 5F - _ 79 - y
12 E $'\022' 2C E \, 46 - F 60 E \` 7A - z
13 E $'\023' 2D - - 47 - G 61 - a 7B E \{
14 E $'\024' 2E - . 48 - H 62 - b 7C E \|
15 E $'\025' 2F - / 49 - I 63 - c 7D E \}
16 E $'\026' 30 - 0 4A - J 64 - d 7E E \~
17 E $'\027' 31 - 1 4B - K 65 - e 7F E $'\177'
18 E $'\030' 32 - 2 4C - L 66 - f
19 E $'\031' 33 - 3 4D - M 67 - g
第一个字段是字节的十六进制值,第二个字段包含 E
,如果字符需要转义,第三个字段显示字符的转义表示。
,
?你可能会看到一些字符不必总是被转义,比如 ,
、}
和 {
。
所以不总是,但有时候:
echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.
或者
echo test { 1, 2, 3 }
test { 1, 2, 3 }
但要注意:
echo test{1,2,3}
test1 test2 test3
echo test\ {1,2,3}
test 1 test 2 test 3
echo test\ {\ 1,\ 2,\ 3\ }
test 1 test 2 test 3
echo test\ {\ 1\,\ 2,\ 3\ }
test 1, 2 test 3
,
的特殊注释。我很惊讶地发现,内置的Bash printf -- %q ','
会给出\,
,但/usr/bin/printf -- %q ','
会给出,
(未转义)。其他字符也是如此:{
、|
、}
、~
。 - kevinarpe@Q
非常有用! - fedorqui@A
正是我所需要的! - Lenormju使用print '%q'
的技巧,我们可以运行循环来查找哪些字符是特殊字符:
#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
char="${special:i:1}"
printf -v q_char '%q' "$char"
if [[ "$char" != "$q_char" ]]; then
printf 'Yes - character %s needs to be escaped\n' "$char"
else
printf 'No - character %s does not need to be escaped\n' "$char"
fi
done | sort
它会输出以下内容:
No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped
其中一些结果,例如,
看起来有点可疑。很有趣能够得到@CharlesDuffy的意见。
%q
不知道您计划在 shell 中使用字符的位置,因此它将转义所有可能在任何可能的 shell 上下文中具有特殊含义的字符。,
本身对于 shell 没有特殊含义,但正如 @F.Hauri 在他的回复中指出的那样,在 {...}
大括号扩展中确实具有特殊含义:https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Brace-Expansion 这就像 !
一样,只需要在特定情况下扩展,而不是在一般情况下扩展:echo Hello World!
可以正常工作,但 echo test!test
将失败。 - Mecki在 Bourne 或 POSIX shell 中需要转义的字符与 Bash 不同。 通常,Bash 是这些 shell 的超集,因此您在 shell
中转义的任何内容都应该在 Bash 中转义。
一个很好的通用规则是“如果不确定,请转义它”。但转义某些字符会赋予它们特殊含义,如 \n
。 这些字符在 man bash
页面下的 Quoting
和 echo
中列出。
除此之外,转义任何非字母数字的字符都更加安全。我不知道确切的列表。man 手册中某处列出了它们,但没有一个地方集中列出。学习语言才是确保正确的方法。
其中一个让我犯错的是 !
。这是 Bash(和 csh)中的特殊字符(历史扩展),但不是 Korn shell 中的特殊字符。即使是 echo "Hello world!"
也会出问题。像往常一样使用单引号可以消除特殊含义。
sed
检查是否需要转义是否足够好。感谢您的回答! - fedorquised
进行检查,您可以使用几乎任何东西进行检查。 sed
不是问题,bash
才是。在单引号内没有特殊字符(除了单引号),甚至无法在其中转义字符。sed
命令通常应该在单引号内,因为RE元字符与shell元字符有太多重叠,以至于不安全。例外情况是嵌入shell变量,这必须小心处理。 - cdarkeecho
命令进行检查。如果输出与输入相同,则无需转义。 :) - Mark Reed我注意到在使用自动完成时,Bash会自动转义一些字符。
例如,如果你有一个名为dir:A
的目录,bash会自动完成为dir\:A
利用这一点,我运行了一些使用ASCII表字符的实验,并得出了以下列表:
Bash在自动完成时需要转义的字符:(包括空格)
!"$&'()*,:;<=>?@[\]^`{|}
bash不转义的字符:
#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
/
,因为它不能用于目录名)printf %q
命令在作为参数传递时修改和不修改哪些字符 - 最好是遍历整个字符集。 - Charles Duffyprintf %q
的有些不同,我在测试包含“home”波浪号的路径名时遇到了这个问题(%q会转义,对我造成了问题,而自动完成则不会)。 - Compholio
sed
,但需要bash
。 - jwdsed
的macOS用户: @fd0有一个sed
选项来转义每个字符:https://apple.stackexchange.com/a/363400/409134 我编写了一个解决方案,只使用perl
转义控制字符:https://apple.stackexchange.com/a/458279/409134 - Nils