如何使用while循环从两个输入文件中读取

jay*_*ngh 33 bash io-redirection shell-script

我想知道是否有任何方法可以一次读取一行嵌套的 while 循环中的两个输入文件。例如,假设我有两个文件FileAFileB.

文件A:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3
Run Code Online (Sandbox Code Playgroud)

文件B:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3
Run Code Online (Sandbox Code Playgroud)

当前示例脚本:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea
Run Code Online (Sandbox Code Playgroud)

执行:

[jaypal:~/Temp] ./read.sh 
this is File A line1
this is File B line1
this is File B line2
this is File B line3
this is File A line2
this is File B line1
this is File B line2
this is File B line3
this is File A line3
this is File B line1
this is File B line2
this is File B line3
Run Code Online (Sandbox Code Playgroud)

问题和所需的输出:

这将针对 FileA 中的每一行完全循环 FileB。我尝试使用 continue、break、exit 但它们都不是为了实现我正在寻找的输出。我希望脚本只从文件 A 中读取一行,然后从文件 B 中读取一行并退出循环并继续执行文件 A 的第二行和文件 B 的第二行。类似于以下脚本的内容 -

[jaypal:~/Temp] cat read1.sh 
#!/bin/bash
count=1
while read lineA
    do echo $lineA 
        lineB=`sed -n "$count"p fileb`
        echo $lineB
        count=`expr $count + 1`
done < filea

[jaypal:~/Temp] ./read1.sh 
this is File A line1
this is File B line1
this is File A line2
this is File B line2
this is File A line3
this is File B line3
Run Code Online (Sandbox Code Playgroud)

这可以用while循环实现吗?

les*_*ana 42

如果您可以保证某些字符永远不会出现在第一个文件中,那么您可以使用 paste。

例如,您肯定知道@永远不会发生:

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done
Run Code Online (Sandbox Code Playgroud)

请注意,如果该字符保证不会出现在第一个文件中就足够了。这是因为在填充最后一个变量时read会忽略IFS。所以即使@发生在第二个文件中,它也不会被拆分。

使用一些 bash 功能来获得更清晰的代码并使用默认分隔符选项卡粘贴的示例:

while IFS=$'\t' read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done < <(paste file1 file2)
Run Code Online (Sandbox Code Playgroud)

使用的 Bash 功能:ansic 字符串( $'\t') 和进程替换( <(...)) 以避免子外壳问题中的 while 循环

如果您不能确定任何字符都不会出现在两个文件中,那么您可以使用两个文件描述符

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done 3<file1 4<file2
Run Code Online (Sandbox Code Playgroud)

测试不多。可能会在空行上中断。

文件描述符编号 0、1 和 2 已分别用于 stdin、stdout 和 stderr。3 及以上的文件描述符(通常)是免费的。bash 手册警告不要使用大于 9 的文件描述符,因为它们是“内部使用的”。

请注意,打开的文件描述符被继承给 shell 函数和外部程序。继承打开文件描述符的函数和程序可以读取(和写入)文件描述符。在调用函数或外部程序之前,您应该注意关闭所有不需要的文件描述符。

这是与上面相同的程序,实际工作(打印)与元工作(并行从两个文件中逐行读取)分开。

work() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  work "$f1" "$f2"
done 3<file1 4<file2
Run Code Online (Sandbox Code Playgroud)

现在我们假设我们无法控制工作代码,并且无论出于何种原因,该代码都会尝试从文件描述符 3 中读取。

unknowncode() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
  read -r yoink <&3 && printf 'yoink: %s\n' "$yoink"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  unknowncode "$f1" "$f2"
done 3<file1 4<file2
Run Code Online (Sandbox Code Playgroud)

这是一个示例输出。请注意,第一个文件的第二行是从循环中“偷来的”。

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2
Run Code Online (Sandbox Code Playgroud)

以下是在调用外部代码(或任何与此相关的代码)之前关闭文件描述符的方法。

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  # this will close fd3 and fd4 before executing anycode
  anycode "$f1" "$f2" 3<&- 4<&-
  # note that fd3 and fd4 are still open in the loop
done 3<file1 4<file2
Run Code Online (Sandbox Code Playgroud)


Gil*_*il' 18

在不同的文件描述符上打开两个文件。将read内置的输入重定向到您想要的文件所连接的描述符。在bash / KSH / zsh中,你可以写read -u 3,而不是read <&3

while IFS= read -r lineA && IFS= read -r lineB <&3; do
  echo "$lineA"; echo "$lineB"
done <fileA 3<fileB
Run Code Online (Sandbox Code Playgroud)

当最短的文件已被处理时,此代码段停止。请参阅将两个文件读入 IFS while 循环——在这种情况下,有没有办法获得零差异结果?如果您想继续处理直到两个文件结束。

另请参阅何时使用额外的文件描述符?有关文件描述符的其他信息,以及为什么经常使用“while IFS= read”而不是“IFS=”;阅读时..`?的解释IFS= read -r


小智 5

尝试以下命令:

paste -d '\n' inp1.txt inp2.txt > outfile.txt
Run Code Online (Sandbox Code Playgroud)