Bash

如何使用while循環從兩個輸入文件中讀取

  • May 6, 2021

我想知道是否有任何方法可以從嵌套的 while 循環中的兩個輸入文件一次讀取一行。例如,假設我有兩個文件FileAFileB.

檔案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循環實現嗎?

如果您可以保證某些字元永遠不會出現在第一個文件中,那麼您可以使用 paste。

例如,您肯定知道這@永遠不會發生:

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 已分別用於標準輸入、標準輸出和標準錯誤。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

引用自:https://unix.stackexchange.com/questions/26601