使用文件的行作为Linux命令的参数
1. 简介
在本教程中,我们将讨论如何使用文件的行作为 Linux 命令的参数。例如,当一个程序返回我们想要用作另一个命令的输入的文件名列表时,这可能很有用。在进入脚本之前,我们还可以使用此处显示的技术来初步自动化具有长参数列表的命令。
2. 设置
本文示例使用的工作目录包含不同的文本文件:
$ tree ./
./
├── arguments1
├── arguments2
├── file10
├── file15
├── file20
└── file5
0 directories, 6 files
名称以file开头后跟数字 N 的四个文件有 N 个单词。也就是说,file5包含五个单词。名为arguments1的文件逐行包含这四个文件的名称。稍后我们将讨论文件参数2 的内容。我们可以使用cat 命令仔细检查第一个带有参数的文件的内容:
$ cat arguments1
file10
file15
file20
file5
我们将遵循的示例基于字数统计命令wc 。该命令可能会根据指定的标志返回文件的字数以及其他输出。根据为本示例选择的文件命名和文件夹结构,我们可以使用 bash globbing 获取每个文件的字数:
$ wc -w file*
10 file10
15 file15
20 file20
5 file5
50 total
在继续讨论不同的方法之前,我们应该澄清 shell 解释参数行的两种方式。
首先,我们假设我们希望在一次调用感兴趣的命令时将所有参数行一起使用。因此,开头介绍的方法返回与 bash globbing 相同的信息(查看最新的代码片段)。
稍后,我们为每一行调用一次命令,使用每一行作为参数。
3. 无空格参数的建议解决方案
在本节中,我们将提出不同的解决方案,以便能够将文件的行用作命令的参数。
3.1. 使用命令cat:一个命令调用所有文件行
第一种方法是使用cat 命令,该命令已用于检查文件arguments1 的内容。一般命令是:
commmand `cat file_with_arguments`
对于命令示例,这会产生与上面相同的结果:
$ wc -w `cat arguments1`
10 file10
15 file15
20 file20
5 file5
50 total
cat 命令的结果也可以使用美元符号和括号进行扩展,这是 POSIX 标准的首选方式,它还提供其他好处 ,例如更好地嵌套和处理反斜杠:
commmand $(cat file_with_arguments)
将cat的结果扩展的第二种方法应用于文章的示例返回:
$ wc -w $(cat arguments1)
10 file10
15 file15
20 file20
5 file5
50 total
3.2. 使用重定向运算符:一个命令调用所有文件行
重定向操作符 是该问题的另一种解决方案,不需要使用cat命令。然而,它的语法类似于前面的过程:
commmand $(< file_with_arguments)
得到的结果与之前的一致:
$ wc -w $(< arguments1)
10 file10
15 file15
20 file20
5 file5
50 total
重定向操作符*<和cat命令之间的区别在于,**使用重定向操作符时,shell 自己执行重定向而不执行cat*二进制文件**。cat命令之所以有效,是因为它具有重定向属性,但它会产生一些开销,因为它需要一个子 shell。
3.3. 使用 for 循环:每个文件行一个命令调用
在某些情况下,以前的方法不起作用,因为需要对命令进行多次调用,每行带有参数的文件调用一次。还有另一种情况,下面显示的方法更好:多个参数想要多次变化,每个参数都有一个文件。
这种方法依赖于for循环来分别使用arguments1文件的每一行调用命令一次 :
for arg in $(< file_with_arguments); do command "$arg"; done
即使以前的方法可以使用wc命令,让我们用for循环解决同样的问题:
$ for arg in $(< arguments1); do wc -w "$arg"; done
10 file10
15 file15
20 file20
5 file5
使用这种方法,我们显式地多次调用命令而不是一次。这就是为什么带有wc命令的for循环没有显示前面方法中出现的*“总共 50”*行:我们用一个文件调用该命令四次(而不是用四个文件调用一次)。
4. 带空格参数的建议解决方案
在本节中,我们将介绍以前方法的局限性以及可以用来解决这些问题的解决方案。
4.1. 先前方法的局限性
对于所有以前的方法,主要限制是 shell 如何处理带有参数的文件行:它还会拆分空格。因此,让我们假设这个文件由一个字符串组成,其中第二个参数有空格:
arg1
"arg 2"
arg3
即使我们用引号之间的空格将字符串括起来,shell 也不会转义该序列。到目前为止,使用所介绍的方法,函数要么接收四个参数,要么我们调用函数四次,每次一个参数:
arg1
"arg
2"
arg3
这可能不是在所有情况下都存在问题,认为以前的方法有效且简单。但是,我们应该牢记避免某些论点出现问题。
我们可以通过考虑另一个带有参数的文件来看到这一点:arguments2。此文件包含与以前相同的参数,但顺序不同:
$ cat arguments2
file10
file15
file20 file5
考虑到行数,命令wc必须被调用 3 次。第一个使用file10作为唯一参数,第二个仅使用file15,最后一个使用两个参数:file20和file5。这样,只有在最后一次调用中,才会出现总字数。
但是,当使用基于for循环的方法时,输出显示该函数为每个文件调用一次:
$ for arg in $(< arguments2); do wc -w "$arg"; done
10 file10
15 file15
20 file20
5 file5
4.2. 使用 While-Read-Do 构造:每个文件行一个命令调用
为了解决这个问题,还有另一个过程可以逐行使用文件的内容作为命令的参数。这是一种允许将行作为完整参数处理的方法,这在某些情况下可能是必要的。
此外,如果我们想同时改变多个参数,这是有利的。以下方法仅需要一个文件(每行中的每个命令调用都使用空格分隔的参数)。同时,for循环过程需要一个文件用于每个参数位置(每个文件的行数与命令调用的行数一样多,但每行只有一个参数)。
该解决方案使用之前使用的 while-read-do 和重定向运算符:
while read arg; do command $arg; done < file_with_arguments
与文件arguments2一起使用的这种方法返回预期的输出:
$ while read arg; do wc -w $arg; done < arguments2
10 file10
15 file15
20 file20
5 file5
25 total
在最后一个输出中,我们看到了最后一个函数调用的总字数,因为它同时收到了两个参数:file20和file5。