tha*_*guy 87 bash shell sh quoting
以下是一系列echo $var
可以显示与刚刚分配的值不同的值的情况.无论指定的值是"双引号","单引号"还是不引用,都会发生这种情况.
如何让shell正确设置我的变量?
星号
预期的输出是/* Foobar is free software */
,但我得到一个文件名列表:
$ var="/* Foobar is free software */"
$ echo $var
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...
Run Code Online (Sandbox Code Playgroud)
方括号
期望值是[a-z]
,但有时我会得到一个字母!
$ var=[a-z]
$ echo $var
c
Run Code Online (Sandbox Code Playgroud)
换行(换行符)
预期值是一个单独的行列表,但所有值都在一行!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
Run Code Online (Sandbox Code Playgroud)
多个空间
我期望一个精心对齐的表头,但是多个空格要么消失要么折叠成一个!
$ var=" title | count"
$ echo $var
title | count
Run Code Online (Sandbox Code Playgroud)
标签
我期望两个制表符分隔值,但我得到两个空格分隔值!
$ var=$'key\tvalue'
$ echo $var
key value
Run Code Online (Sandbox Code Playgroud)
tha*_*guy 108
在上述所有情况中,变量都已正确设置,但未正确读取!正确的方法是在引用时使用双引号:
echo "$var"
Run Code Online (Sandbox Code Playgroud)
这给出了给出的所有示例中的期望值.总是引用变量引用!
为什么?
当变量未加引号时,它将:
进行字段拆分,其中值在空格上拆分为多个单词(默认情况下):
之前: /* Foobar is free software */
后:/*
,Foobar
,is
,free
,software
,*/
这些单词中的每一个都将经历路径名扩展,其中模式扩展为匹配文件:
之前: /*
后:/bin
,/boot
,/dev
,/etc
,/home
,...
最后,所有参数都传递给echo,它将它们用单个空格分隔出来,给出
/bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/
Run Code Online (Sandbox Code Playgroud)
而不是变量的值.
当引用变量时,它将:
这就是为什么你应该总是引用所有变量引用,除非你特别需要单词拆分和路径名扩展.像shellcheck这样的工具可以提供帮助,并会在上述所有情况下警告缺少引号.
fed*_*qui 14
您可能想知道为什么会这样.再加上其他人的精彩解释,找到为什么我的shell脚本会在空格或其他特殊字符上窒息?由Gilles在Unix和Linux中编写:
我为什么要写
"$foo"
?没有报价会怎么样?
$foo
并不意味着"获取变量的值foo
".这意味着更复杂的事情:
- 首先,取变量的值.
- 字段拆分:将该值视为以空格分隔的字段列表,并构建结果列表.例如,如果该变量包含
foo * bar ?
然后此步骤的结果为第3元素的列表foo
,*
,bar
.- 文件名生成:将每个字段视为glob,即作为通配符模式,并将其替换为与此模式匹配的文件名列表.如果模式与任何文件都不匹配,则保持不变.在我们的示例中,这导致列表包含
foo
,后面是当前目录中的文件列表,最后是bar
.如果当前目录是空的,结果是foo
,*
,bar
.请注意,结果是字符串列表.shell语法中有两种上下文:list context和string context.字段拆分和文件名生成仅发生在列表上下文中,但这是大部分时间.双引号分隔字符串上下文:整个双引号字符串是单个字符串,不能拆分.(例外:
"$@"
扩展到位置参数列表,例如"$@"
相当于"$1" "$2" "$3"
有三个位置参数.请参阅$*和$ @之间的区别?)命令用
$(foo)
或用命令替换也是如此`foo`
.请注意,不要使用`foo`
:它的引用规则很奇怪且不可移植,$(foo)
除了具有直观的引用规则外,所有现代shell都支持绝对等效.算术替换的输出也经历了相同的扩展,但这通常不是问题,因为它只包含不可扩展的字符(假设
IFS
不包含数字或-
).看什么时候需要双引号?有关可以省略引号的情况的更多详细信息.
除非你的意思是发生所有这些乱七八糟的事情,否则请记住在变量和命令替换周围总是使用双引号.请注意:省略引号不仅会导致错误,还会导致 安全漏洞.
除了其他问题造成无法引用,-n
并且-e
可以通过消耗echo
作为参数。(根据POSIX规范echo
,只有前者是合法的,但是几种常见的实现方式也违反了该规范并消耗-e
)。
为了避免这种情况,使用printf
而不是echo
在细节问题。
从而:
$ vars="-e -n -a"
$ echo $vars # breaks because -e and -n can be treated as arguments to echo
-a
$ echo "$vars"
-e -n -a
Run Code Online (Sandbox Code Playgroud)
但是,使用时,正确的引用并不一定能节省您的时间echo
:
$ vars="-n"
$ echo $vars
$ ## not even an empty line was printed
Run Code Online (Sandbox Code Playgroud)
......而这会节省你printf
:
$ vars="-n"
$ printf '%s\n' "$vars"
-n
Run Code Online (Sandbox Code Playgroud)