私がシェルスクリプトを使用している場合、私がしていることの大部分は他のモジュールのI/Oをpythonやmatlabなどでラップしています。これを行うには、通常、テキストファイルまたはそのような性質の入出力パスを使用します。使用できる1つのファイルから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で行うための標準的な方法は何ですか?
_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 <&3
_は、ファイル記述子3から_line1
_を読み取ります。これは、_read -u3 line1
_と同等に書き込むこともできます。
for file in $(cat $1);
などのステートメントには、知っておくべき問題がいくつかあります。シェルは、Word分割パス名展開をファイルの内容に適用します。これを予期していない限り、さまざまなエラーが発生する可能性があります。
_while read line1 <&3 && read line2 <&4
do
echo "line1=$line1 and line2=$line2"
done 3<file1 4<file2
_
ファイルの行を反復するには:
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
を使用すると、最長のファイルと一致するように、空の行で最短のファイルが完成します。どちらかのファイルの終わりに達したらすぐに終了するには、&&
ではなく||
を使用します。すべてのケースを検出する場合は、戻りコードを個別に確認してください。
{
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
または、2つのファイルを結合することもできます。 paste
コマンドが便利です。デフォルトでは、行をタブで区切り(-d
を渡して別の区切り文字を選択します)、空の行でファイルを完成させます。ファイルにタブが含まれていない場合、これによって入力行が明確に区切られます。
tab=$(printf \\t)
paste input-file1 input-file2 |
while IFS=$tab read -r line1 line2; do … done
シェルはテキスト処理を高速で実行できないことに注意してください。中規模から大規模な入力には、より専門的なツールが最適です。 paste
による前処理は、後処理のために2つのファイルをZip圧縮するのに便利です。行が読み取られるタイミングをさらに制御する必要がある場合、awkはgetline
コマンドを使用してそれを実行できます(シェルのread
と同様)。