1. 概要

このチュートリアルでは、 ls -lコマンドの出力を反復処理する方法を説明します。 デフォルトでは、コマンドの出力を読み取るときに、行は単語の境界によって分割されます。 ただし、改行文字に到達するまで、行全体を処理することを期待しています。 このデフォルトの動作を変更する方法を見ていきます。

2. 問題

まず、目前の問題を見てみましょう。 3つのファイルがあるとします。

$ ls -l
total 16
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

単純なforループを使用して、 ls-lコマンドの結果を反復処理してみましょう。

$ for line in $(ls -l); do echo $line; done
total
20
-rw-r--r--
1
bluelake
bluelake
3278
Feb
11
17:01
test1.txt
-rw-r--r--
1
bluelake
bluelake
3227
Feb
11
17:01
test2.txt
-rw-r--r--
1
bluelake
bluelake
7392
Feb
11
17:01
test3.txt

ここでは、結果が単語の境界によって異なる行に分割されていることがわかります。 出力からの各単語が取得され、標準出力に送信されます。

明らかに、これは私たちが必要としているものではありません。 それでは、これを修正するさまざまな方法を見てみましょう。

3. IFS変数の変更

上記のように、単語で分割する理由は、Internal Field Separator(IFS)変数がデフォルト値に設定されているためです。 また、デフォルト値では、単語を空白で分割します。

これを新しい行に変更して、どのように機能するかを見てみましょう。

$ IFS='
> '
$ for line in `ls -l`; do echo $line; done
total 20
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

上記の結果は、単語ごとに処理するのではなく、1行全体を正しく処理したことを示しています。

ただし、このように IFS 変数の値を変更すると、同じセッションで実行されるすべてのコマンドに影響します。

これを回避するために、これをサブシェルで実行できます。

$ (IFS='
> '
> for line in `ls -l`; do echo $line; done)
total 20
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

ここで、 for ループだけを再度実行すると、このセッションの IFS 変数を変更しなかったため、各単語が1行に出力されます。

4. readコマンドの使用

ご存知のように、 read コマンドを使用して、標準入力から行を読み取り、それを単語に分割することができます。 それでは、ニーズに合わせてreadコマンドを使用する方法を見てみましょう。

$ ls -l | while read line; do echo $line; done
total 20
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

readコマンドが行全体を行変数に読み込むことがわかります。そしてその変数から、echoコマンドを使用して各行を標準出力に出力できました。

5. awkコマンドの使用

awk は、テキストとストリームを処理するためのすばらしいユーティリティです。 この特定のタスクにawkコマンドを使用する方法を見てみましょう。

$ ls -l | awk '{print $0}'
total 24
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

上記の結果から、コマンド出力のすべての行を繰り返し処理したことがわかります。 ここでは、awkコマンドの$0オプションを使用して、レコード全体を印刷しました

ご存知のように、 awk コマンドには、インデックスを指定して1つ以上の列を選択するオプションがあります。 このように、出力で必要な列のみを選択することにより、の柔軟性が向上します。

6. xargsコマンドの使用

xargs コマンドを使用すると、標準の入力から引数を取得して、別のコマンドに提供できることがわかります。 これにxargsコマンドを使用する方法を確認しましょう。

$ ls -l | xargs -I{} echo "{}"
total 24
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

結果から、各行が個別に処理されていることがわかります。

-Iオプションを使用して、一度に1行を出力からフェッチしました。 次に、その行を処理できます。 ここでは、ラインを標準出力にエコーしました。

7. parallelコマンドの使用

最後に、GNU parallel コマンドを使用できます。このコマンドは、タスクを並列に実行して、あるタスクが別のタスクの開始を待たないようにします。 その例を見てみましょう:

$ ls -l | parallel --jobs 4 echo
total 20
-rw-r--r-- 1 bluelake bluelake 3278 Feb 11 17:01 test1.txt
-rw-r--r-- 1 bluelake bluelake 3227 Feb 11 17:01 test2.txt
-rw-r--r-- 1 bluelake bluelake 7392 Feb 11 17:01 test3.txt

ここでは、ジョブオプションを使用して4つの並列ジョブを実行しました。 これは、複数の大きなファイルを並行して処理する場合に便利です

8. 結論

このチュートリアルでは、lsコマンドの出力を反復処理するさまざまな方法を見てきました。 これはlsコマンドからの出力に対して行われますが、出力を1行ずつ読み取る必要がある場合は、他のコマンドにも同じ手法を適用できます。