提问人:Cristian 提问时间:4/9/2012 最后编辑:codeforesterCristian 更新时间:6/14/2023 访问量:156336
何时将引号括起来?
When to wrap quotes around a shell variable?
问:
我是否应该或不应该在 shell 脚本中的变量周围使用引号?
例如,以下内容是否正确:
xdg-open $URL
[ $? -eq 2 ]
或
xdg-open "$URL"
[ "$?" -eq "2" ]
如果是这样,为什么?
答:
一般规则:如果它可以是空的或包含空格(或任何空格)或特殊字符(通配符),请引用它。不用空格引用字符串通常会导致 shell 将单个参数拆分为多个参数。
$?
不需要引号,因为它是一个数值。是否需要它取决于你允许什么,以及如果它是空的,你是否仍然想要一个参数。$URL
出于习惯,我总是倾向于引用字符串,因为这样更安全。
评论
IFS=0
echo $?
cp $source1 $source2 $dest
dest
source1
source2
quote it if...
有倒退的思维过程 - 引号不是你需要添加的东西,而是你需要删除的东西。始终将字符串和脚本括在单引号中,除非您需要使用双引号(例如,让变量扩展)或不需要使用引号(例如,进行通配和文件名扩展)。
简而言之,引用不需要 shell 执行分字和通配符扩展的所有内容。
单引号逐字保护它们之间的文本。当您需要确保外壳根本不接触字符串时,它是合适的工具。通常,当您不需要变量插值时,它是首选的报价机制。
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
当需要可变插值时,双引号是合适的。通过适当的调整,当您需要字符串中的单引号时,这也是一种很好的解决方法。(没有直接的方法可以在单引号之间转义单引号,因为单引号内没有转义机制——如果有,它们不会完全逐字引用。
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
当您特别要求 shell 执行分字和/或通配符扩展时,不使用引号。
单词拆分(又名令牌拆分);
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
相比之下:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(循环仅在单个带引号的字符串上运行一次。
$ for word in '$words'; do echo "$word"; done
$words
(循环仅在文本单引号字符串上运行一次。
通配符扩展:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
相比之下:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(没有从字面上命名的文件。file*.txt
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(也没有文件名!$pattern
更具体地说,任何包含文件名的内容通常都应该用引号引起来(因为文件名可以包含空格和其他 shell 元字符)。任何包含 URL 的内容通常都应该被引用(因为许多 URL 包含 shell 元字符,如 和 )。任何包含正则表达式的内容通常都应该被引用(同上)。除非空格字符之间的单个空格外,任何包含重要空格的内容都需要引用(因为否则,shell 会有效地将空格混入单个空格,并修剪任何前导或尾随空格)。?
&
当您知道变量只能包含不包含 shell 元字符的值时,引号是可选的。因此,不带引号的变量基本上没问题,因为这个变量只能包含一个数字。但是,也是正确的,并建议保持一般的一致性和正确性(尽管这是我个人的建议,而不是广泛认可的政策)。$?
"$?"
不是变量的值基本上遵循相同的规则,尽管您也可以转义任何元字符而不是引用它们。对于一个常见的示例,除非元字符被转义或引用,否则 shell 将解析包含 a 的 URL 作为后台命令:&
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(当然,如果 URL 位于不带引号的变量中,也会发生这种情况。对于静态字符串,单引号最有意义,尽管任何形式的引用或转义在这里都有效。
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
最后一个例子还提出了另一个有用的概念,我喜欢称之为“跷跷板引用”。如果需要混合使用单引号和双引号,可以将它们彼此相邻使用。例如,以下带引号的字符串
'$HOME '
"isn't"
' where `<3'
"' is."
可以背靠背粘贴在一起,在标记化和删除报价后形成一个长字符串。
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
这不是很清楚,但这是一种常见的技术,因此很高兴知道。
顺便说一句,脚本通常不应该将 ls
用于任何内容。要扩展通配符,只需 ...使用它。
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(在后一个示例中,循环是完全多余的; 具体适用于多个参数。 太。但是遍历通配符匹配是一个常见问题,而且经常做错了。printf
stat
包含要循环的令牌列表或要扩展的通配符的变量很少见,因此我们有时缩写为“引用所有内容,除非您确切地知道自己在做什么”。
评论
NULL="$(php -r 'echo chr(0);')"
printf "%s" "$NULL" | wc -c
以下是一般报价的三点公式:
双引号
在我们想要抑制单词拆分和通配的上下文中。此外,在我们希望将文字视为字符串而不是正则表达式的上下文中。
单引号
在字符串文字中,我们希望抑制插值和对反斜杠的特殊处理。换言之,使用双引号是不合适的情况。
无引号
在我们绝对确定不存在单词拆分或通配问题的上下文中,或者我们确实想要单词拆分和通配。
例子
双引号
- 带有空格 (,
"StackOverflow rocks!"
"Steve's Apple"
) - 变量扩展 (,
"$var"
"${arr[@]}"
) - 命令替换 (,
"$(ls)"
"`ls`"
) - 目录路径或文件名部分包含空格 (
"/my dir/"*
) - 保护单引号 (
"single'quote'delimited'string"
) - Bash 参数扩展 (
"${filename##*/}"
)
单引号
- 命令名称和包含空格的参数
- 需要插入才能抑制的文本字符串 ( ,
'Really costs $$!'
'just a backslash followed by a t: \t'
) - 保护双引号 (
'The "crux"'
) - 需要禁止插值的正则表达式文本
- 对涉及特殊字符 (
$'\n\t'
) - 在我们需要保护多个单引号和双引号的地方使用 shell 引号 (
$'{"table": "users", "where": "first_name"=\'Steve\'}'
)
无引号
- 围绕标准数值变量(、 等)
$$
$?
$#
- 在算术上下文中,如 、 、
((count++))
"${arr[idx]}"
"${string:start:length}"
- 内部表达,没有分词和通配问题(这是一个风格问题,意见可能会有很大差异)
[[ ]]
- 我们想要分词的地方(
for word in $words
) - 我们想要通配的地方 (
for txtfile in *.txt; do ...
) - 我们希望被解释为 ( 但不是
~
$HOME
~/"some dir"
"~/some dir"
)
另请参阅:
评论
"ls" "/"
[[ ]]
=
==
=~
$'...'
"ls" "/"
ls /
case
我通常使用引号 like 来表示安全,除非我确定它不包含空格。"$var"
$var
我确实使用一种简单的方法来连接行:$var
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
评论
*
评论