jay*_*ngh 33 bash io-redirection shell-script
我想知道是否有任何方法可以一次读取一行嵌套的 while 循环中的两个输入文件。例如,假设我有两个文件FileA和FileB.
文件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。