提问人:that other guy 提问时间:4/1/2015 最后编辑:that other guy 更新时间:4/24/2022 访问量:365244
我只是分配了一个变量,但 echo $variable 显示了其他内容
I just assigned a variable, but echo $variable shows something else
问:
以下是一系列情况,其中可以显示与刚刚分配的值不同的值。无论分配的值是“双引号”、“单引号”还是未加引号,都会发生这种情况。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
答:
在上述所有情况下,变量设置正确,但未正确读取!正确的方法是在引用时使用双引号:
echo "$var"
这给出了所有给定示例中的期望值。始终引用变量引用!
为什么?
当变量未加引号时,它将:
进行字段拆分,其中值在空格上拆分为多个单词(默认情况下):
以前:
/* Foobar is free software */
后:
/*
Foobar
is
free
software
*/
这些单词中的每一个都将经历路径名扩展,其中模式被扩展为匹配的文件:
以前:
/*
后:。。。
/bin
/boot
/dev
/etc
/home
最后,所有参数都传递给 echo,echo 将它们写出来,用单个空格分隔,给出
/bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/
而不是变量的值。
当变量被引用时,它将:
- 替换其值。
- 没有步骤 2。
这就是为什么您应该始终引用所有变量引用的原因,除非您特别要求单词拆分和路径名扩展。像 shellcheck 这样的工具可以提供帮助,并且会在上述所有情况下警告缺少引号。
评论
$(..)
var=$(cat file; printf x); var="${var%x}"
除了将变量放在引号中之外,还可以使用空格将变量的输出转换为换行符。tr
$ echo $var | tr " " "\n"
foo
bar
baz
虽然这有点复杂,但它确实增加了输出的多样性,因为您可以将任何字符替换为数组变量之间的分隔符。
评论
tr
tr
IFS=$'\n' read -r -d '' -a arrayname < <(cat file.txt && printf '\0')
cat
$'\n'
$'\t'
tr
(或者 ,就此而言,它可以很好地工作;人们可以直接使用,它会以同样的方式被破坏,但效率较低)。arrayname=( $( cat file | tr '\n' ' ' ) )
*
cat
arrayname=$( $(<file) )
您可能想知道为什么会发生这种情况。连同那个人的精彩解释,找到一个参考资料 为什么我的 shell 脚本会窒息在空格或其他特殊字符上? 由 Gilles 在 Unix 和 Linux 中编写:
为什么我需要写?没有引号会发生什么?
"$foo"
$foo
并不意味着“取变量的值”。这意味着 更复杂的东西:foo
- 首先,取变量的值。
- 字段拆分:将该值视为以空格分隔的字段列表,并生成结果列表。例如,如果变量 包含,则此步骤的结果是 3 元素 列表。
foo * bar
foo
*
bar
- 文件名生成:将每个字段视为一个 glob,即通配符模式,并将其替换为与此匹配的文件名列表 模式。如果模式与任何文件都不匹配,则保留该模式 未修改。在我们的示例中,这会导致包含 的列表, 接下来是当前目录中的文件列表,最后是 .如果当前目录为空,则结果为 、 、 。
foo
bar
foo
*
bar
请注意,结果是字符串列表。在 shell 语法:列表上下文和字符串上下文。字段拆分和 文件名生成仅在列表上下文中发生,但这是大多数 时间。双引号分隔字符串上下文:整体 双引号字符串是单个字符串,不可拆分。(例外:扩展到位置参数列表,例如 是 相当于如果有三个位置 参数。请参阅 $* 和 $@ 有什么区别?
"$@"
"$@"
"$1" "$2" "$3"
)使用或使用 的命令替换也会发生同样的情况。顺便说一句,不要使用 :它的引用规则是 奇怪且不可移植,所有现代 shell 都支持 除了有直观的报价规则外,是绝对等价的。
$(foo)
`foo`
`foo`
$(foo)
算术代换的输出也经历相同 扩展,但这通常不是问题,因为它只包含 不可展开的字符(假设不包含数字或)。
IFS
-
请参阅何时需要双引号? 您可以省略引号的情况。
除非你的意思是让所有这些僵化发生,否则请记住 始终在变量和命令替换两边使用双引号。做 注意:省略引号不仅会导致错误,还会导致安全性 孔。
echo $var
输出高度依赖于变量的值。默认情况下,它包含空格、制表符和换行符:IFS
[ks@localhost ~]$ echo -n "$IFS" | cat -vte
^I$
这意味着当 shell 进行字段拆分(或单词拆分)时,它使用所有这些字符作为单词分隔符。当引用一个没有双引号的变量来回显它()时,就会发生这种情况,因此预期的输出会改变。$var
防止单词拆分的一种方法(除了使用双引号)是设置为 null。查看 http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :IFS
如果IFS为null,则不进行字段拆分。
设置为 null 意味着设置为空 价值:
IFS=
测试:
[ks@localhost ~]$ echo -n "$IFS" | cat -vte
^I$
[ks@localhost ~]$ var=$'key\nvalue'
[ks@localhost ~]$ echo $var
key value
[ks@localhost ~]$ IFS=
[ks@localhost ~]$ echo $var
key
value
[ks@localhost ~]$
评论
set -f
IFS
echo $var
echo '/* Foobar is free software */'
mkdir "/this thing called Foobar is free software etc/"
[a-z]
[a-z]
root@ubuntu:/home/qgb# var=32321 echo $var root@ubuntu:/home/qgb# var=3231; echo $var 3231
用户双引号可获取确切值。喜欢这个:
echo "${var}"
它将正确读取您的值。
评论
除了引用失败导致的其他问题外,还可以作为参数使用。(根据 POSIX 规范,只有前者是合法的,但一些常见的实现也违反了规范并消耗)。-n
-e
echo
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
但是,在使用以下方法时,正确的引用并不总是能挽救您:echo
$ vars="-n"
$ echo "$vars"
$ ## not even an empty line was printed
...而它将为您节省:printf
$ vars="-n"
$ printf '%s\n' "$vars"
-n
评论
-e
-n
-n
吗?
-e
echo
-n
-e
ks1322 的答案帮助我在使用时识别了问题:docker-compose exec
如果省略该标志,请添加一个特殊字符来破坏输出,我们看到而不是:-T
docker-compose exec
b
1b
$ test=$(/usr/local/bin/docker-compose exec db bash -c "echo 1")
$ echo "${test}b"
b
echo "${test}" | cat -vte
1^M$
使用标志,按预期工作:-T
docker-compose exec
$ test=$(/usr/local/bin/docker-compose exec -T db bash -c "echo 1")
$ echo "${test}b"
1b
评论
var=$(cat file)
echo "$var"