Bashのテキストファイルから複数行を読む

Bashのテキストファイルから複数行を読む

シェルスクリプトを書くときに私がすることのほとんどは、Python、Matlabなどの他のモジュールからI / Oをラップすることです。この目的のために、私は通常、入力/出力パスを持つテキストファイルまたは同様の性質のファイルを使用します。私は利用可能なファイルから1行を読む方法を知っています。

for file in $(cat $1);
do
    code using $file
done

しかし、両方のファイルの同じ行を使用して作業を実行するにはどうすればよいですか? Javaに似ています。

while((line1 = file1.readLine()) != null) {
    line2 = file2.readLine();
    //do something with both lines...
}

Bashでこれを行う標準的な方法は何ですか?

答え1

exec 3<file1
exec 4<file2
while read line1 <&3 && read line2 <&4
do
        echo "line1=$line1 and line2=$line2"
done
exec 3<&-
exec 4<&-

議論する

  • 上記では、入力行から先行スペースと末尾スペースを削除しました。このスペースを維持するには、read …次のように置き換えます。IFS= read …

  • 上記では、入力のバックスラッシュはエスケープ文字として解釈されます。これをしたくない場合は、次のようread …に交換してください。read -r …

  • read line1 <&3line1ファイル記述子 3 から読み込みます。これは次のように等しく書くこともできますread -u3 line1

  • このような声明にはfor file in $(cat $1);知っておくべきいくつかの問題があります。シェルはファイルの内容にトークン化されたパス名拡張を適用するため、予期しない限りさまざまなエラーが発生する可能性があります。

選ぶ

while read line1 <&3 && read line2 <&4
do
        echo "line1=$line1 and line2=$line2"
done 3<file1 4<file2

答え2

ファイルの行を繰り返すには、次のようにします。

while IFS= read -r line; do
  echo "read $line"
done <input-file

複数のファイルを繰り返すには、別のファイル記述子でそのファイルを開きます(参照追加のファイル記述子はいつ使用されますか?)。

while IFS= read -r line1 <&8 || IFS= read -r line2 <&9; do
  echo "read '$line1' from file 1 and '$line2' from file 2"
done 8<input-file1 9<input-file2

最長ファイルと一致するように、空行でread <&8 || read <&9最短ファイルを完成させます。 2つのファイルのいずれかの終わりに達したらすぐに終了するには、代わりに&&を使用します||。すべてのケースを検出するには、個別に戻りコードを確認してください。

{
  while
    IFS= read -r line1 <&8; empty1=$?
    IFS= read -r line2 <&9; empty2=$?
    [ "$empty1" -ne 0 ] && [ "$empty2" -ne 0 ]
  do
    echo "read '$line1' from file 1 and '$line2' from file 2"
  done
  if [ "$empty1" -ne 0 ]; then
    echo "Finishing processing file 1"
  fi
  if [ "$empty2" -ne 0 ]; then
    echo "Finishing processing file 2"
  fi
} 8<input-file1 9<input-file2

あるいは、両方のファイルを一緒にリンクすることもできます。これpasteこれにはコマンドが便利です。デフォルトでは、タブごとに行を区切って(-d別の区切り文字を選択するには渡します)、空白行でファイルを完成します。ファイルにタブ文字が含まれていない場合、入力行は明確に区別されます。

tab=$(printf \\t)
paste input-file1 input-file2 |
while IFS=$tab read -r line1 line2; do … done

シェルはテキスト処理速度がそれほど速くはありません。中大型入力には、より専門的なツールが最も適しています。前処理を使用すると、paste後処理のために2つのファイルを簡単に圧縮できます。行を読み取るときにさらに制御が必要な場合、awkはそのコマンドを使用してこれを実行できますgetline(シェルのように)。read

関連情報