web-dev-qa-db-ja.com

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

ネストされたwhileループ内の2つの入力ファイルから一度に1行を読み取る方法があるかどうかを知りたいと思いました。たとえば、2つのファイルFileAFileBがあるとします。

FileA:

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

FileB:

[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ループで実現できますか?

27
jaypal singh

最初のファイルで一部の文字が発生しないことが確実にわかっている場合は、貼り付けを使用できます。

デフォルトの区切り記号タブを使用した貼り付けの例:

_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
_

最初のファイルで文字が発生しないことが保証されていれば十分であることに注意してください。これは、最後の変数を入力するときにreadIFSを無視するためです。したがって、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 string (_$'\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
_

あまりテストされていません。空の行で壊れる可能性があります。

ファイル記述子番号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
_
33
lesmana

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

このスニペットは、最短のファイルが処理されると停止します。 2つのファイルをIFSのwhileループに読み込む-この場合、diffの結果がゼロになる方法はありますか? 両方のファイルの終わりまで処理を継続する場合。

ファイル記述子の詳細については、 追加のファイル記述子をいつ使用しますか? を参照してください 「IFS =の代わりに、「IFS =読み取り中」が頻繁に使用されるのはなぜですか? ..`?IFS= read -rの説明。

シェルスクリプトが必要なことはわかっていますが、pasteコマンドを確認することをお勧めします。

4
lutzky

以下のコマンドを試してください:

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

あるいは、bashのmapfileコマンドを使用して、ファイルの各行をarray [line_of_file_index]に結び付ける配列変数にSlurpできると思います。しかし、それがBash3より高いか、Bash4のみに対するものかはわかりません。

http://wiki.bash-hackers.org/commands/builtin/mapfile

0
Nikhil Mulley