是什么决定了程序参数需要如何分隔?

是什么决定了程序参数需要如何分隔?

我注意到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")执行系列扩展。具体来说:

  1. 该命令被分解为标记。我不会详细讨论这些规则;令牌基本上是标点符号或不包含顶级空格的字符序列。这里,标记是字符串/bin/kill和命令替换$(echo "75341 75342"),它本身是由$(…)运算符和标记echo和构建的75341 75342
  2. 执行命令替换运算符中的命令。这是一个简单的命令,带有命令名称echo和单个参数75341 75342。 (引号是 shell 语法的一部分,它们分隔一个字符串,该字符串成为命令的参数。)
  3. 该命令的输出是75341 75342␤换行符。 shell 获取此输出并去掉最后的换行符,生成字符串75341 75342
  4. 因为命令替换运算符在列表上下文中使用(在双引号之外),所以它会经历分词文件名扩展。字拆分包括获取字符串并根据变量的值将其拆分为字符串列表IFS。默认情况下,IFS包含空格、制表符和换行符,因此字符串会以这些字符的任意顺序进行分割:它变成两个字符串75341和的列表75342。文件名扩展在这里不会改变任何内容。
  5. 我们现在有一个包含三个字符串的列表:/bin/kill7534175342。这是作为/bin/kill带有两个参数75341和 的命令执行的75342

使用命令/bin/kill $(echo -e "75577\n75578"),扩展几乎是相同的。步骤 3 产生输出75341␤75342␤。在步骤 4 中,单词拆分会生成与之前相同的 list 7534175342因为换行符和空格是同等有效的单词分隔符。因此步骤 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

相关内容