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


27

我想知道是否有任何一种方式可以同时嵌套嵌套循环读取两个输入文件。例如,假设我有两个文件FileAFileB

FileA:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3

文件B:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3

当前示例脚本:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea

执行:

[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

问题和期望的输出:

对于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

使用while循环可以实现吗?


通过很好的解决方案@codaddict是在这里:stackoverflow.com/a/4011824/4095830 - >paste -d '\n' file1 file2
whoan

Answers:


32

如果您确定在第一个文件中永远不会出现某些字符,则可以使用粘贴。

使用默认定界符选项卡进行粘贴的示例:

paste file1 file2 | while IFS="$(printf '\t')" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

使用示例粘贴@

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

请注意,只要保证该字符不会出现在第一个文件中就足够了。这是因为填充最后一个变量时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)

使用的Bash功能:ansi c字符串$'\t')和进程替换<(...))以避免subshel​​l问题中的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

没有测试太多。空行可能会中断。

文件描述符编号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

现在,我们假装我们无法控制工作代码,并且无论出于何种原因,该代码都试图从文件描述符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

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

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2

这是在调用外部代码(或与此有关的任何代码)之前应关闭文件描述符的方式。

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

17

在不同的文件描述符上打开两个文件。将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

处理了最短的文件后,此代码段将停止。请参阅将两个文件读入IFS while循环中-在这种情况下是否有办法使差异结果为零?如果要一直处理到两个文件的末尾。

另请参见何时使用附加文件描述符?有关文件描述符的其他信息,以及为什么经常使用while IFS = read而不是IFS =;在阅读时。为解释IFS= read -r


感谢@Gilles提供有关文件描述符的其他链接。
jaypal singh 2011年

@Gilles也许我误解了您,但是我无法使循环过程完全成为最长的文件(在我的情况下,它始终是$ fileA),所以我将其变成一个单独的问题,就是:有没有办法编写循环,所以差异没有注意到输入和输出之间的任何区别?unix.stackexchange.com/questions/26780/…我能得到的最接近的区别是仅发现差异的一行。
ixtmixilix 2011年


2

请尝试以下命令:

paste -d '\n' inp1.txt inp2.txt > outfile.txt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.