我注意到kill
实用程序能够接受由空格字符和换行符分隔的参数:
$ sleep 120 & sleep 240 &
[1] 75341
[2] 75342
$ /bin/kill $(echo "75341 75342")
[1]- Terminated: 15 sleep 120
[2]+ Terminated: 15 sleep 240
$ sleep 120 & sleep 240 &
[1] 75577
[2] 75578
$ /bin/kill $(echo -e "75577\n75578")
[1]- Terminated: 15 sleep 120
[2]+ Terminated: 15 sleep 240
$
我是否正确,如果它接受例如由换行符分隔的参数,这完全取决于实用程序本身?
答案1
由于您不引用$(echo -e "75577\n75578")
,因此由 shell 来处理它。
shell 解析它得到的内容的方式取决于名为 IFS(内部字段分隔符)的变量。默认情况下,它包含空格、制表符和换行符,这意味着这三个字符的任何组合都被视为参数之间的有效分隔符。
如果您将其设置为不包含换行符的字符串,您的命令将收到\n
嵌入的参数,然后将失败:
$ touch a b
$ echo "$IFS" | od -c
0000000 \t \n \n
0000004$
$ ls -l a b
-rw-r--r-- 1 jlliagre jlliagre 0 Nov 12 15:04 a
-rw-r--r-- 1 jlliagre jlliagre 0 Nov 12 15:04 b
$ ls -l $(printf "a\nb")
-rw-r--r-- 1 jlliagre jlliagre 0 Nov 12 15:05 a
-rw-r--r-- 1 jlliagre jlliagre 0 Nov 12 15:05 b
$ IFS=" "
$ ls -l $(printf "a\nb")
ls: cannot access a
b: No such file or directory
$
答案2
实用程序的参数作为字符串列表,而不是作为单个字符串。因此,该实用程序在参数之间没有任何分隔符的概念。参数只是列表中的单独元素,它们之间没有任何内容。
将字符串拆分为参数列表的实体是 shell。 shell 执行命令行就像/bin/kill $(echo "75341 75342")
执行系列扩展。具体来说:
- 该命令被分解为标记。我不会详细讨论这些规则;令牌基本上是标点符号或不包含顶级空格的字符序列。这里,标记是字符串
/bin/kill
和命令替换$(echo "75341 75342")
,它本身是由$(…)
运算符和标记echo
和构建的75341 75342
。 - 执行命令替换运算符中的命令。这是一个简单的命令,带有命令名称
echo
和单个参数75341 75342
。 (引号是 shell 语法的一部分,它们分隔一个字符串,该字符串成为命令的参数。) - 该命令的输出是
75341 75342
换行符
。 shell 获取此输出并去掉最后的换行符,生成字符串75341 75342
。 - 因为命令替换运算符在列表上下文中使用(在双引号之外),所以它会经历分词和文件名扩展。字拆分包括获取字符串并根据变量的值将其拆分为字符串列表
IFS
。默认情况下,IFS
包含空格、制表符和换行符,因此字符串会以这些字符的任意顺序进行分割:它变成两个字符串75341
和的列表75342
。文件名扩展在这里不会改变任何内容。 - 我们现在有一个包含三个字符串的列表:
/bin/kill
、75341
和75342
。这是作为/bin/kill
带有两个参数75341
和 的命令执行的75342
。
使用命令/bin/kill $(echo -e "75577\n75578")
,扩展几乎是相同的。步骤 3 产生输出7534175342
。在步骤 4 中,单词拆分会生成与之前相同的 list 75341
,75342
因为换行符和空格是同等有效的单词分隔符。因此步骤 5 执行完全相同的命令。
正如您所看到的,决定分隔参数的步骤是 shell 执行的分词步骤。您可以通过更改 的值来尝试此步骤IFS
。例如,这再次产生相同的命令:
IFS=+
/bin/kill $(echo "75341+75342")
答案3
我认为这是$( )
用空格代替换行的结构。只需使用echo $(echo -e "75577\n75578")
.
现在尝试
cat <<EOF | kill
1234
5678
EOF
(具有适当的值)。
答案4
这并不完全是关于kill
,即使使用 (ie)ls
你也会有这种行为:
> touch file1 file2
> ls $(echo -e "file1 file2")
file1 file2
> ls $(echo -e "file1\nfile2")
file1 file2
这是因为它与命令本身无关,而是与 bash 如何处理脚本有关。如果您从“脚本”方面来看,我的意思会更清楚。尝试将命令重定向到文件内,将其 chmod 为 777 并执行:
> touch file1 file2
> ls $(echo -e "file1\nfile2")
file1 file2
> echo -e 'ls $(echo -e "file1\nfile2")' > tryme.sh
> cat tryme.sh
ls $(echo -e "file1
file2")
> chmod 777 tryme.sh
> ./tryme.sh
file1 file2