为什么 shell 处理 $( 的部分输出

为什么 shell 处理 $( 的部分输出

我在阅读时看到了这一行关于 IFS 的博客那是 :

for i in $(<test.txt)

并认为将$(<test.txt)文件内容打印到 STDOUT。我可能错了,但出于好奇,我尝试在 shell 上做到这一点。因此,选择了一个名为array具有随机数据的随机文件,并且

首先做了一个cat array给了我这个:

amit@C0deDaedalus:~/test$ 
amit@C0deDaedalus:~/test$ cat array
1)      Ottawa  Canada          345644
2)      Kabul   Afghanistan     667345
3)      Paris   France          214423
4)      Moscow  Russia          128793
5)      Delhi   India           142894

然后$(<array)这给了我这个:

amit@C0deDaedalus:~/test$ $(<array)
1)      Ottawa  Ca: command not found

我只知道它<用于输入重定向,但没有确切地了解 shell 在这里将其解释为命令的内容。

谁能解释一下 shell 中这个奇怪的输出背后的概念吗?

更新 :-

运行时set -x它给出:

amit@C0deDaedalus:~/test$ $(<array)
+ '1)' Ottawa Canada 345644 '2)' Kabul Afghanistan 667345 '3)' Paris France 214423 '4)' Moscow Russia 128793 '5)' Delhi India 142894
+ '[' -x /usr/lib/command-not-found ']'
+ /usr/lib/command-not-found -- '1)'
1): command not found
+ return 127
amit@C0deDaedalus:~/test$ 

答案1

$(command)语法在子 shell 环境中执行command,并将其自身替换为command.和,正如 Bash 手册所说$(< file)只是更快的等效项$(cat file)(但这不是 POSIX 功能)。

因此,当您运行 时$(<array),Bash 会执行该替换,然后它使用第一个字段作为命令的名称,其余字段作为命令的参数:

$ $(<array)
1): command not found

我没有任何1)命令/函数,因此它会打印一条错误消息。

但在您的特定场景中,您可能会收到不同的错误消息,因为您修改了 IFS 变量:

$ IFS=n; $(<array)
1)      Ottawa  Ca: command not found

编辑1

我的猜测是你的IFS代码被修改了,所以这就是为什么你的 shell 尝试执行1) Ottawa Ca而不是1).毕竟,您正在阅读一篇IFS相关文章。如果你IFS最终得到一个奇怪的值,我不会感到惊讶。

IFS变量控制所谓的分词或者场分裂。它基本上定义了 shell 在扩展上下文中(或其他命令,如 )如何解析数据read

Bash 手册解释了这个主题:

3.5.7 分词

shell 扫描未出现在双引号内的参数扩展、命令替换和算术扩展的结果以进行分词。

shell 将 的每个字符$IFS视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。如果IFS未设置,或者其值恰好是<space><tab><newline>默认值,则忽略先前扩展结果的开头和结尾处的<space><tab>、 和序列,并且不在开头或结尾处的任何字符序列用于分隔单词。如果具有默认值以外的值,则只要空白字符位于(空白字符)的值中,单词开头和结尾处的空白字符、、 和的序列就会被忽略。其中的任何非空白字符以及任何相邻的空白字符都会分隔字段。空白字符序列也被视为分隔符。如果 的值为空,则不会发生分词。<newline>IFSIFSspacetabnewlineIFSIFSIFSIFSIFSIFSIFS

显式空参数(""'')将被保留并作为空字符串传递给命令。由于扩展没有值的参数而产生的不带引号的隐式空参数将被删除。如果没有值的参数在双引号内展开,则会产生空参数,该参数将被保留并作为空字符串传递给命令。当带引号的空参数作为扩展非空的单词的一部分出现时,空参数将被删除。也就是说,该单词在分词和删除空参数之后-d''变为。-d

请注意,如果没有发生扩展,则不会执行拆分。

以下是有关IFS命令替换用法的一些示例:

示例1:

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' ${var}
[hello]
[world]

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' "${var}"
[hello     world]

在这两种情况下,IFSis <space><tab><newline>(默认值),varishello world并且有一个printf语句。但请注意,在第一种情况下会执行分词,而在第二种情况下则不会(因为双引号会抑制该行为)。分词发生在非引用扩展中。

示例2:

$ IFS='x'; var='fooxbar'; printf '[%s]\n' ${var}
[foo]
[bar]

$ IFS='2'; (exit 123); printf '[%s]\n' ${?}
[1]
[3]

既不${var}也不${?}包含任何空白字符,因此人们可能会认为在这种情况下分词不会成为问题。但事实并非如此,因为它IFS可能会被滥用。IFS几乎可以保存任何值并且很容易被滥用。

示例3:

$ $(echo uname)
Linux

$ $(xxd -p -r <<< 64617465202d75)
Sat Apr 28 12:46:49 UTC 2018

$ var='echo foo; echo bar'; eval "$(echo "${var}")"
foo
bar

这与分词无关,但请注意我们如何使用一些肮脏的技巧来注入代码。

相关问题:

相关内容