何时在shell变量周围包装引号?

时间:2012-04-08 23:02:08

标签: linux bash shell unix quotes

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

例如,以下是正确的:

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

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

如果是这样,为什么?

5 个答案:

答案 0 :(得分:85)

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

$?不需要引号,因为它是一个数值。 $URL是否需要它取决于你在那里允许的内容,以及如果它是空的,你还是想要一个参数。

我倾向于总是出于习惯而引用字符串,因为它更安全。

答案 1 :(得分:68)

简而言之,引用您不需要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的任何内容(因为许多网址包含?&等shell元字符。通常应该引用任何包含正则表达式的东西(同上)。除了非空格字符之间的单个空格之外,任何包含重要空格的内容都需要被引用(因为否则,shell会将空格强制转换为有效的单个空格,并修剪任何前导或尾随空格。)

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

非变量的值基本上遵循相同的规则,但您也可以转义任何元字符而不是引用它们。对于一个常见示例,除非元字符被转义或引用,否则shell将解析其中包含&的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.

这不是非常清晰,但这是一种常见的技术,因此很有用。

另外,脚本should usually not use ls for anything.要扩展通配符,只需...使用它。

$ 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。但是通过通配符匹配是一个常见问题,而且经常做错了。)

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

答案 2 :(得分:14)

以下是一般报价的三点公式:

双引号

在我们想要抑制单词拆分和通配的上下文中。同样在我们希望将文字视为字符串而不是正则表达式的上下文中。

单引号

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

没有引号

在我们完全确定没有单词分裂或全局问题的情况下,或者我们想要分词和整合

<强>实施例

双引号

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

单引号

  • 其中包含空格的命令名称和参数
  • 需要抑制插值的文字字符串('Really costs $$!''just a backslash followed by a t: \t'
  • 保护双引号('The "crux"'
  • 要求插值被抑制的正则表达式文字
  • 对涉及特殊字符($'\n\t'
  • 的文字使用shell引用
  • 使用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"

另见:

答案 3 :(得分:1)

除非我确定"$var"不包含空格,否则我通常会使用类似$var的引用,除非我确定$var不包含空格。

我使用lines="`cat multi-lines-text-file.txt`" echo "$lines" ## multiple lines echo $lines ## all spaces (including newlines) are zapped 作为连接线的简单方法:

In [114]: df[unique_count[unique_count == unique_count.min()].index].melt(
                var_name='column', value_name='value')
Out[114]:
  column  value
0      C      1
1      C      1
2      C      2
3      D      3
4      D      3
5      D      4

答案 4 :(得分:-1)

要在shell脚本中使用变量,请使用带引号的变量“”,因为该变量可以包含空格或特殊字符,而这些字符或特殊字符不会影响shell脚本的执行。否则,如果您确定变量名中没有空格或特殊字符,则可以在不带“”的情况下使用它们。

示例:

回显“ $ url name”-(可以一直使用)

回显“ $ url name”-(在这种情况下不能使用,因此在使用前请多加注意)