我刚刚分配了一个变量,但是echo$variable显示了其他内容

col17t5w  于 2021-06-03  发布在  Sqoop
关注(0)|答案(7)|浏览(373)

以下是一系列的案例 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
lbsnaicq

lbsnaicq1#

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
yqkkidmi

yqkkidmi2#

echo $var 产量在很大程度上取决于产品的价值 IFS 变量。默认情况下,它包含空格、制表符和换行符:

[ks@localhost ~]$ echo -n "$IFS" | cat -vte
 ^I$

这意味着当shell进行字段拆分(或分词)时,它使用所有这些字符作为分词符。当引用一个没有双引号的变量来回显它时,就会发生这种情况( $var )从而改变了预期的产出。
防止分词的一种方法(除了使用双引号)是设置 IFS 设置为空。看到了吗http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :
如果ifs的值为空,则不进行字段拆分。
设置为空表示设置为空值:

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 ~]$
p4rjhz4m

p4rjhz4m3#

在上述所有情况下,变量设置正确,但读取不正确!正确的方法是在引用时使用双引号:

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/

而不是变量的值。
当引用变量时,它将:
被它的价值所取代。
没有第二步。
这就是为什么您应该总是引用所有变量引用,除非您特别要求分词和路径名扩展。像shellcheck这样的工具可以提供帮助,并警告上面所有情况下的引号缺失。

zengzsys

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 不包含数字或 - ).
看什么时候需要双引号?有关案例的更多详细信息,请忽略引号。
除非您的意思是要让所有这些繁琐的事情发生,否则请记住总是在变量和命令替换之间使用双引号。注意:省略引号不仅会导致错误,还会导致安全漏洞。

uajslkp6

uajslkp65#

使用双引号获得确切的值。这样地:

echo "${var}"

它会正确读取你的值。

ahy6op9u

ahy6op9u6#

除了将变量放在引号中,还可以使用 tr 以及将空格转换为换行符。

$ echo $var | tr " " "\n"
foo
bar
baz

尽管这有点复杂,但它确实增加了输出的多样性,因为您可以将任何字符替换为数组变量之间的分隔符。

yvt65v4c

yvt65v4c7#

除未报价引起的其他问题外, -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

然而,正确的引用并不总是节省你使用时 echo :

$ vars="-n"
$ echo $vars
$ ## not even an empty line was printed

…但它会拯救你 printf :

$ vars="-n"
$ printf '%s\n' "$vars"
-n

相关问题