从 bash 中的文本文件中读取多行

从 bash 中的文本文件中读取多行

当我编写 shell 脚本时,我所做的大部分工作是将其他模块的 I/O 包装在 python、matlab 等中。为此,我通常使用文本文件或具有输入/输出路径的类似性质的文件。我知道从我可以使用的一个文件中读取一行,

for file in $(cat $1);
do
    code using $file
done

但是如果我想使用两个文件中的等效行执行某些操作怎么办?类似于 Java 的等效项:

while((line1 = file1.readLine()) != null) {
    line2 = file2.readLine();
    //do something with both lines...
}

在 bash 中执行此操作的标准方法是什么?

答案1

exec 3<file1
exec 4<file2
while read line1 <&3 && read line2 <&4
do
        echo "line1=$line1 and line2=$line2"
done
exec 3<&-
exec 4<&-

讨论

  • 在上面,从输入行中去除了前导和尾随空白。如果你想保留这个空格,请替换read …IFS= read …

  • 在上面,输入中的反斜杠将被解释为转义字符。如果您不想这样做,请替换read …read -r …

  • read line1 <&3line1从文件描述符 3读取。这也可以等效地写为read -u3 line1

  • 诸如此类的陈述for file in $(cat $1);有一些您应该了解的问题。 shell 会将分词路径名扩展应用于文件的内容,除非您期望如此,否则可能会导致各种错误。

选择

while read line1 <&3 && read line2 <&4
do
        echo "line1=$line1 and line2=$line2"
done 3<file1 4<file2

答案2

要迭代文件的行:

while IFS= read -r line; do
  echo "read $line"
done <input-file

要迭代多个文件,请在不同的文件描述符上打开它们(请参阅什么时候会使用额外的文件描述符?)。

while IFS= read -r line1 <&8 || IFS= read -r line2 <&9; do
  echo "read '$line1' from file 1 and '$line2' from file 2"
done 8<input-file1 9<input-file2

使用read <&8 || read <&9空行完成最短的文件以匹配最长的文件。要在到达任一文件末尾时立即退出,请使用&&而不是||.如果要检测所有情况,请单独检查返回码。

{
  while
    IFS= read -r line1 <&8; empty1=$?
    IFS= read -r line2 <&9; empty2=$?
    [ "$empty1" -ne 0 ] && [ "$empty2" -ne 0 ]
  do
    echo "read '$line1' from file 1 and '$line2' from file 2"
  done
  if [ "$empty1" -ne 0 ]; then
    echo "Finishing processing file 1"
  fi
  if [ "$empty2" -ne 0 ]; then
    echo "Finishing processing file 2"
  fi
} 8<input-file1 9<input-file2

或者,您可以将两个文件连接在一起。这paste命令对此很方便。默认情况下,它通过制表符分隔行(传递-d以选择不同的分隔符)并用空行完成文件。如果文件不包含制表符,则会明确界定输入行。

tab=$(printf \\t)
paste input-file1 input-file2 |
while IFS=$tab read -r line1 line2; do … done

请注意,shell 进行文本处理的速度不是很快。更专业的工具最适合中型到大型输入。预处理paste可以方便地将两个文件压缩在一起以进行任何后处理。如果您需要更多地控制何时读取行, awk 可以使用其getline命令(类似于 shell 的read)来做到这一点。

相关内容