我想知道是否有任何方法可以在嵌套 while 循环中一次读取两个输入文件。例如,假设我有两个文件FileA
和FileB
.
文件A:
[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 循环来实现吗?
答案1
如果您可以保证某些字符永远不会出现在第一个文件中,那么您可以使用粘贴。
例如,您确信这@
永远不会发生:
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'
) 和流程替代(<(...)
) 到避免子 shell 中的 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
答案2
在不同的地方打开这两个文件文件描述符。将内置的输入重定向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
。
答案3
尝试以下命令:
paste -d '\n' inp1.txt inp2.txt > outfile.txt
答案4
或者,我想您可以使用 bash 的 mapfile 命令将文件放入数组变量中,将文件的每一行绑定到 array[line_of_file_index] 中。但是,我不确定它是否仅适用于 Bash3 更高版本或 Bash4。