以下是一系列的案例 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 ...
方括号
期望值为 [a-z]
但有时我只收到一封信!
$ var=[a-z]
$ echo $var
c
换行符(换行符)
期望值是一个单独行的列表,但是所有值都在一行上!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
多个空格
我希望有一个仔细对齐的表头,但相反,多个空格要么消失,要么被折叠成一个!
$ var=" title | count"
$ echo $var
title | count
标签
我期望有两个制表符分隔的值,但是我得到了两个空格分隔的值!
$ var=$'key\tvalue'
$ echo $var
key value
7条答案
按热度按时间lbsnaicq1#
ks1322的答案帮助我在使用
docker-compose exec
:如果你忽略了
-T
旗帜,docker-compose exec
添加一个特殊的字符,中断输出,我们看到b
而不是1b
:与
-T
旗帜,docker-compose exec
按预期工作:yqkkidmi2#
echo $var
产量在很大程度上取决于产品的价值IFS
变量。默认情况下,它包含空格、制表符和换行符:这意味着当shell进行字段拆分(或分词)时,它使用所有这些字符作为分词符。当引用一个没有双引号的变量来回显它时,就会发生这种情况(
$var
)从而改变了预期的产出。防止分词的一种方法(除了使用双引号)是设置
IFS
设置为空。看到了吗http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :如果ifs的值为空,则不进行字段拆分。
设置为空表示设置为空值:
测试:
p4rjhz4m3#
在上述所有情况下,变量设置正确,但读取不正确!正确的方法是在引用时使用双引号:
这给出了所有示例中的期望值。总是引用变量引用!
为什么?
当变量不带引号时,它将:
执行字段拆分,将值拆分为多个空白字(默认情况下):
之前:
/* Foobar is free software */
之后:/*
,Foobar
,is
,free
,software
,*/
这些单词中的每一个都将进行路径名扩展,其中模式扩展为匹配的文件:之前:
/*
之后:/bin
,/boot
,/dev
,/etc
,/home
, ...最后,将所有参数传递给echo,echo将它们用单个空格隔开,给出
而不是变量的值。
当引用变量时,它将:
被它的价值所取代。
没有第二步。
这就是为什么您应该总是引用所有变量引用,除非您特别要求分词和路径名扩展。像shellcheck这样的工具可以提供帮助,并警告上面所有情况下的引号缺失。
zengzsys4#
你可能想知道为什么会这样。加上另一个家伙的精彩解释,找到一个关于为什么我的shell脚本会被空格或其他特殊字符阻塞的参考?gilles在unix和linux中编写:
为什么我要写“$foo”?没有引号怎么办?
$foo
并不意味着“取变量的值foo
”. 它意味着更复杂的事情:首先,获取变量的值。
字段拆分:将该值视为以空格分隔的字段列表,并构建结果列表。例如,如果变量包含
foo * bar
然后这个步骤的结果就是3元素列表foo
,*
,bar
.文件名生成:将每个字段视为一个glob,即通配符模式,并将其替换为与此模式匹配的文件名列表。如果模式与任何文件都不匹配,则保持不变。在我们的示例中,这将导致包含
foo
,后面是当前目录中的文件列表,最后是bar
. 如果当前目录为空,则结果为foo
,*
,bar
.请注意,结果是一个字符串列表。shell语法中有两个上下文:列表上下文和字符串上下文。字段拆分和文件名生成只在列表上下文中发生,但大多数情况下都是这样。双引号分隔字符串上下文:整个双引号字符串是单个字符串,不能拆分(例外情况:
"$@"
展开到位置参数列表,例如。"$@"
相当于"$1" "$2" "$3"
如果有三个位置参数。看到$*和$@有什么区别了吗?)同样的情况也发生在命令替换中
$(foo)
或与foo
. 在旁注上,不要使用foo
:它的引用规则很奇怪,不可移植,所有现代shell都支持$(foo)
这是绝对相等的,除了有直观的引用规则。算术替换的输出也会经历相同的扩展,但这通常不是一个问题,因为它只包含不可扩展的字符(假设
IFS
不包含数字或-
).看什么时候需要双引号?有关案例的更多详细信息,请忽略引号。
除非您的意思是要让所有这些繁琐的事情发生,否则请记住总是在变量和命令替换之间使用双引号。注意:省略引号不仅会导致错误,还会导致安全漏洞。
uajslkp65#
使用双引号获得确切的值。这样地:
它会正确读取你的值。
ahy6op9u6#
除了将变量放在引号中,还可以使用
tr
以及将空格转换为换行符。尽管这有点复杂,但它确实增加了输出的多样性,因为您可以将任何字符替换为数组变量之间的分隔符。
yvt65v4c7#
除未报价引起的其他问题外,
-n
以及-e
可以被消耗echo
作为论据(根据posix规范,只有前者是合法的echo
,但有几个常见的实现违反了规范并使用-e
以及)。要避免这种情况,请使用
printf
而不是echo
当细节重要的时候。因此:
然而,正确的引用并不总是节省你使用时
echo
:…但它会拯救你
printf
: