当前位置: 首页 > 知识库问答 >
问题:

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

司寇阳朔
2023-03-14

这里有一系列情况,其中echo$var可以显示与刚刚分配的值不同的值。无论分配的值是“双引号”、“单引号”还是未引号,都会发生这种情况。

如何让 shell 正确设置我的变量?

星号

预期的输出是/*Foobar是免费软件*/,但我得到了一个文件名列表:

$ var="/* Foobar is free software */"
$ echo $var 
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...

方括号

期望值是< code>[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

共有3个答案

于恺
2023-03-14

除了未引用导致的其他问题外,< code>-n和< code>-e可以被< code>echo用作参数。(根据< code>echo的POSIX规范,只有前者是合法的,但是有几个常见的实现违反了规范并消耗了< code>-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
乐正宏深
2023-03-14

您可能想知道为什么会发生这种情况。结合其他人的精彩解释,找到Gilles在Unix中编写的为什么我的外壳脚本会被空格或其他特殊字符阻塞?的参考资料

< code>$foo并不意味着“取变量< code>foo的值”。它意味着更复杂的东西:

  • 首先,获取变量的值
  • 字段拆分:将该值视为以空格分隔的字段列表,并构建结果列表。例如,如果变量包含foo*bar​则此步骤的结果是3元素列表foo*bar
  • 文件名生成:将每个字段视为一个glob,即通配符模式,并将其替换为与此模式匹配的文件名列表。如果模式与任何文件都不匹配,则保持不变。在我们的示例中,这会导致包含foo的列表,然后是当前目录中的文件列表,最后是bar。如果当前目录为空,则结果为foo*bar

请注意,结果是字符串列表。shell 语法中有两个上下文:列表上下文和字符串上下文。字段拆分和文件名生成仅在列表上下文中发生,但大多数情况下都是如此。双引号分隔字符串上下文:整个双引号字符串是单个字符串,不可拆分。(例外:“$@”扩展到位置参数列表,例如,如果有三个位置参数,则“$@”等效于“$1”“$2”“$3”。请参阅 $* 和 $@之间有什么区别?

$(foo)'foo'替换命令也是如此。顺便说一句,不要使用'foo':它的引用规则很奇怪且不可移植,所有现代shell都支持$(foo),这是绝对等价的,除了有直观的引用规则。

算术替换的输出也经历了相同的扩展,但这通常不是问题,因为它只包含不可扩展的字符(假设IFS不包含数字或-)。

请参阅何时需要双引号?有关可以省略引号的情况的更多详细信息。

除非您的意思是让所有这些繁琐的事情发生,否则请记住始终在变量和命令替换之间使用双引号。注意:省略引号不仅会导致错误,还会导致安全漏洞。

上官树
2023-03-14

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

echo "$var"

这给出了所有示例中的预期值。总是引用变量引用!

为什么?

当变量未被引用时,它将:

>

  • 进行字段拆分,其中值在空格上拆分为多个单词(默认情况下):

    之前: /* 福吧是自由软件 */

    之后: /*福巴免费软件*/

    这些单词中的每一个都将经历路径名扩展,其中模式被扩展为匹配的文件:

    之前:< code>/*

    之后:< code>/bin,< code>/boot,< code>/dev,< code>/etc,< code>/home,...

    最后,所有的参数都被传递给echo,它将它们写出,用单个空格分隔,给出

    /bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/
    

    而不是变量的值。

    当变量被引用时,它将:

    1. 被替换为它的值。
    2. 没有步骤2.

    这就是为什么您应该始终引用所有变量引用,除非您特别需要单词拆分和路径名扩展。shellcheck等工具可以提供帮助,并会警告上述所有情况下缺少引号。

  •  类似资料:
    • 问题内容: 有谁知道如何修理它。我正在使用Mac OS 10.8.2 问题答案: 在Python 3中是一个函数;它应该是: 正确安装或使用新版本(如果存在错误)。 在Python 3.3上工作正常。

    • 致命错误:Uncaught ArgumentCounter错误:array_merge()不接受C:\xampp\htdocs\wp includes\widgets中的未知命名参数。php:1265堆栈跟踪:#0 C:\xampp\htdocs\wp includes\widgets.php(1265):array_merge(wp_inactive_widgets:array,sidebar-

    • 问题内容: 我只是运行了一个简单的MySQL CREATE TABLE语句,该语句产生了这一行 “查询确定,受影响的行为0,警告为1(0.07秒)。” 但是,它实际上并没有告诉我警告是什么。您如何查看刚刚发生的警告的内容?我正在使用MySQL 5.1,如果有帮助的话。我在网上唯一发现的是“ SHOW WARNINGS;但这只会产生 “空集(0.00秒)。” 问题答案: 是我知道的唯一方法,但是必须

    • 我刚刚从Windows 8升级到Windows 10(都是64位),我之前的带有用于Android开发的ADT插件的Eclipse Juno不再启动。 我所能看到的只是鼠标指针显示某些内容正在加载几秒钟,然后没有其他反应。 我所尝试的 < li >重命名< code>eclipse.ini以让它重新创建它:甚至没有重新创建它,所以将其重新命名。 < li >在Eclipse exe上打开了“Win

    • 米哈游系统策划,刚刚收到了笔试暂不匹配的邮件通知,想知道友友们情况如何。

    • 失败:生成失败,出现异常。 出了什么问题:任务'执行失败:app: check DebugAarMetadata'。 发生多个任务操作失败:执行com时发生故障。Android建筑格雷德尔。内部的任务。CheckAarMetadataWorkAction