Bash:从多个文件重定向输入
发布于 2008 年 10 月 14 日
最近我需要创建一个处理两个输入文件的脚本。我说的处理是指脚本需要从一个文件获取一行,然后从第二个文件获取一行,然后对它们进行一些操作。听起来很简单,但除非您了解 bash 的一些扩展重定向功能,否则并不那么容易。
为了这个例子,我们假设我们要实现一个简单的paste命令作为 bash 脚本。该paste命令从每个输入文件读取一行,然后将它们粘贴在一起,并将组合结果作为单行写入 stdout。我们的示例版本只对两个输入文件执行此操作。此外,它不会进行任何错误检查,并且会假定文件包含相同数量的行。
我们的输入文件,file1和file2是
$ cat file1 f1 1 f1 2 f1 3 f1 4 $ cat file2 f2 1 f2 2 f2 3 f2 4
您的第一个想法可能是这样的
#!/bin/bash
while read f1 <$1
do
read f2 <$2
echo $f1 $f2
done
$ sh paste-bad.sh file1 file2 f1 1 f2 1 f1 1 f2 1 f1 1 f2 1 f1 1 f2 1 f1 1 f2 1 f1 1 f2 1 f1 1 f2 1 ... Ctrl-C这是因为此处的每个重定向都是重新开始的:它会重新打开文件并读取第一行,然后您会得到一个无限循环。
您的下一个想法可能是逐个读取文件,然后获取缓冲的数据并在之后将它们粘贴在一起
#!/bin/bash
i=0
while read line
do
f1[$i]="$line"
let i++
done <$1
i=0
while read line
do
f2[$i]="$line"
let i++
done <$2
i=0
while [[ "${f1[$i]}" ]]
do
echo ${f1[$i]} ${f2[$i]}
let i++
done
$ sh paste-ok.sh file1 file2 f1 1 f2 1 f1 2 f2 2 f1 3 f2 3 f1 4 f2 4但是,如果您尝试做一些比粘贴行更复杂的事情,那么这种方法可能不可行,而且无论如何都很麻烦。
另一个解决方案是使用一些更高级的重定向
#!/bin/bash
while read f1 <&7
do
read f2 <&8
echo $f1 $f2
done \
7<$1 \
8<$2
在这个版本中,在循环结束时,我们使用 bash 输入重定向的完整通用形式来指定多个输入重定向[n]<word。如果没有指定前导 *[n]*,则默认为 0,这是正常的 stdin 重定向。但是,通过在重定向前面指定一个小整数,我们可以将多个输入文件重定向到命令,在本例中,命令是 *while* 循环
... done \ 7<$1 \ 8<$2这会导致 "while" 循环执行,其中文件描述符 7 打开以读取第一个输入文件,文件描述符 8 打开以读取第二个输入文件。通常,您应该使用大于 2 的数字,因为 0-2 用于 stdin、stdout 和 stderr。
为了让read命令工作,我们需要使用 bash 重定向的另一种形式,在本例中,我们使用 bash 复制文件描述符的能力(如 C 库函数dup2())。文件描述符复制允许两个文件描述符引用同一个打开的文件。由于read通常从 stdin 读取,而不是文件描述符 7 或 8,我们需要一种在 stdin 上复制文件描述符 7(或 8)的方法,bash 的文件描述符复制正是这样做的
while read f1 <&7 ... read f2 <&8 ...请注意read还包括一个-u选项,用于指定要从中读取的文件描述符(如果您喜欢)。
Bash 包含类似形式的输出文件重定向。有关更多信息,请参阅 bash 手册页。