whileループを使用して2つの入力ファイルを読み取る方法

whileループを使用して2つの入力ファイルを読み取る方法

ネストされたwhileループで2つの入力ファイルを同時に読み取る方法があるかどうか疑問に思います。たとえば、2つのファイル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が完全に繰り返されます。継続、中断、終了を試してみましたが、それらのどれも私が望む結果を得ませんでした。スクリプトがファイルAから1行だけ読み込み、ファイルBから1行を読み取り、ループを終了してファイルAの2行目とファイルBの2行目を読み続けたいと思います。次のスクリプトに似たもの -

[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したがって、2番目のファイルで発生しても@分割されません。

よりきれいなコードを取得するためにいくつかの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')とプロセスの交換<(...))到着サブシェルでのwhileループの問題の防止

両方のファイルにどの文字も表示されないかどうかわからない場合は、2つのファイルを使用できます。ファイル記述子

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より大きいファイル記述子を使用しないように警告します。

開かれたファイル記述子は、シェル機能と外部プログラムに継承されます。開かれたファイル記述子を継承する関数とプログラムは、ファイル記述子を読み書きできます。関数または外部プログラムを呼び出す前に、不要なファイル記述子をすべて閉じるように注意する必要があります。

これは、実際のジョブ(印刷)がメタジョブ(2つのファイルを1行ずつ並列に読み取るジョブ)とは別の上記と同じプログラムです。

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

これは出力例です。最初のファイルの2行目はループで「盗まれました」。

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

おすすめ記事