何时在shell变量中引用引号?

有人能告诉我是否应该在shell脚本中将引号包含在变量中吗?

例如,以下是正确的:

xdg-open $URL 
[ $? -eq 2 ]

要么

xdg-open "$URL"
[ "$?" -eq "2" ]

如果是这样,为什么?


一般规则:如果它可以是空的或包含空格(或任何空格)或特殊字符(通配符),则引用它。 不用空格引用字符串通常会导致shell将单个参数拆分为许多。

$? 不需要引号,因为它是一个数字值。 $URL是否需要它取决于您允许的内容以及是否仍然需要参数(如果它为空)。

由于这种方式更安全,我倾向于总是引用字符串。


简而言之,引用您不需要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

(没有文件名为literally file*.txt 。)

$ ls '$pattern'
ls: cannot access $pattern: No such file or directory

(没有名为$pattern文件!)

更具体地说,任何包含文件名的文件都应该被引用(因为文件名可以包含空格和其他shell元字符)。 任何包含URL的地方通常都应该引用(因为许多URL包含shell元字符,如?& )。 任何包含正则表达式的东西都应该被引用(同上)。 除了非空白字符之间的单个空格之外,任何含有重要空格的东西都需要引用(否则,shell会将空白切入单个空格,并修剪任何前导或尾部空格)。

当你知道一个变量只能包含一个不包含shell元字符的值时,引用是可选的。 因此,一个没有引号的$? 基本上没问题,因为这个变量只能包含一个数字。 但是, "$?" 也是正确的,并建议一般性的一致性和正确性(尽管这是我个人的建议,而不是广泛认可的政策)。

不是变量的值基本上遵循相同的规则,尽管您也可以转义任何元字符而不是引用它们。 对于一个常见的例子,除非元字符被转义或引用,否则带有&的URL将被shell解析为后台命令:

$ 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 '%sn' $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: %sn' "$file"
> done
Found file: file1.txt
Found file: file_other.txt

(在后面的例子中循环是完全多余的; printf特别适用于多个参数stat 。但是循环遍历匹配是一个常见问题,并且经常不正确地执行。)

包含要循环使用的标记列表的变量或扩展的通配符不太常见,所以我们有时缩写为“引用所有内容,除非您确切知道自己在做什么”。


一般来说,这里是一个三点的引用公式:

双引号

在我们想要抑制分词和通配的情况下。 在上下文中,我们希望文字被视为字符串,而不是正则表达式。

单引号

在字符串文字中,我们想要抑制反斜线的插值和特殊处理。 换句话说,使用双引号的情况是不合适的。

没有引号

在我们绝对确信没有任何分词或通配问题的情况下,或者我们希望分词和匹配。


例子

双引号

  • 带空格的字符串( "StackOverflow rocks!""Steve's Apple"
  • 变量扩展( "$var""${arr[@]}"
  • 命令替换( "$(ls)""`ls`"
  • 其中目录路径或文件名部分包含空格的球体( "/my dir/"*
  • 保护单引号( "single'quote'delimited'string"
  • Bash参数扩展( "${filename##*/}"
  • 单引号

  • 命令名称和参数中有空格
  • 需要插值的文本字符串被压制( 'Really costs $$!''just a backslash followed by at: t'
  • 保护双引号( 'The "crux"'
  • 需要插值的正则表达式文本被抑制
  • 使用shell引用涉及特殊字符的文字( $'nt'
  • 在我们需要保护几个单引号和双引号的地方使用shell引用( $'{"table": "users", "where": "first_name"='Steve'}'
  • 没有引号

  • 围绕标准数字变量( $$$?$#等)
  • ((count++))"${arr[idx]}""${string:start:length}"等算术语境中
  • [[ ]]表达式内部,不存在分词和通配问题(这是一个风格和意见可能差异很大的问题)
  • 我们想要分词的地方( for word in $words
  • 我们需要globbing( for txtfile in *.txt; do ...
  • 我们想要~被解释为$HOME~/"some dir"而不是"~/some dir"

  • 也可以看看:

  • Bash中单引号和双引号之间的区别
  • 什么是特殊的美元符号shell变量?
  • 链接地址: http://www.djcxy.com/p/17481.html

    上一篇: When to wrap quotes around a shell variable?

    下一篇: I just started learning Scripting in Shell Script, RHEL 6.3