同時実行を行う方法は、いくつかあります。バックグラウンド処理、xargsコマンド、findコマンドのexecオプションを利用することは良く知られている方法になります。
また、パイプを作成することで入力を同時にフィルタすることも可能です。
バックグラウンド処理
バックグラウンド処理の利点には、まず非同期処理が挙げられます。これにより、シェルやターミナルが他のコマンドの終了を待たずに次の作業に進むことができ、作業の効率が向上します。同時に、バックグラウンド処理は複数のタスクを同時に実行できるため、マルチタスキングが可能となります。また、シェルが他のコマンドを待たずに利用可能な状態を維持できます。
バックグラウンド処理を行う場合は、標準出力そのままだとシェルのプロンプトに処理中に出てきた文字列が表示されてしまいます。入力のときに邪魔なので、出力はファイルにするか/dev/nullに捨てるとよいでしょう。
1 2 3 |
$ ( sleep 30; seq 1 100; ) > result.txt 2>&1 & [1] 4053 $ |
終了時に何かメッセージを出したい場合はAND-ORと一緒にバックグラウンド処理にしても良いかもしれません。以下は終了ステータスにかかわらずメッセージを出します。
1 2 3 |
$ ( sleep 30; seq 1 100; ) > result.txt && echo 'true_finish' || echo 'false_finish' & [1] 4110 $ |
補足として、バックグラウンド処理を行うには、フォアグラウンド処理中にCtrl+zで一度ジョブを中断させ、bgコマンドでバックグラウンド処理する方法もあります。
また、端末のセッションが切れても処理を続けたい場合はnohupコマンドを使うとよいでしょう。ただし、nohupコマンドを利用する場合、上のような複合コマンドでは使えないので、その時はシェルスクリプトにして実行します。
xargsコマンドとfindコマンド
xargsコマンドは、標準入力から受け取ったデータを他のコマンドの引数として渡すことができます。
これにより、大量のファイルやディレクトリに対して一括操作に便利となります。
ここでは、簡単にechoコマンドの簡単な出力の例を示します。
1 2 3 4 |
$ seq 1 3 | xargs -I{} echo 'file{}.txt' file1.txt file2.txt file3.txt |
findコマンドの-execオプションについて、これはfindコマンドで見つけた各ファイルに対して指定したコマンドを実行できます。
1 2 3 4 |
$ find . -name '*.txt' -exec ls -l {} \; -rw-rw-r-- 1 ubuntu ubuntu 0 Jan 22 20:28 ./file3.txt -rw-rw-r-- 1 ubuntu ubuntu 0 Jan 22 20:28 ./file2.txt -rw-rw-r-- 1 ubuntu ubuntu 0 Jan 22 20:28 ./file1.txt |
パイプ&フィルタ
パイプをmknodコマンドで作成して、そのパイプを利用してコマンドをバックグラウンド処理するとフィルタとして用いることができます。
これは入力の生成に時間がかかるときに利用できます。しかし、パイプの生成処理や後処理、フィルタ作成の手間や後処理が少し面倒です。
例えば、以下のデータは性別、年齢、A~Dのグループ、乱数になっています。
data.txt
1 2 3 4 5 6 7 8 9 10 |
M,25,A,432 F,32,C,786 M,45,B,234 F,28,A,567 M,40,D,123 F,22,C,890 M,50,B,321 F,20,D,678 M,35,A,456 F,48,C,789 |
上のデータを使い方としては少し非効率ですが、パイプとフィルタを用いてグループごとに並べたいと思います。
前処理や後処理を考えるとシェルスクリプトでまとめたほうが都合がよいので、script.shというスクリプトを作成して実行させたいと思います。
script.shは以下のようになります。
script.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#!/bin/bash # パイプ作成の前チェック if [[ -e inPipeB || \ -e inPipeC || \ -e inPipeD || \ -e outPipeB || \ -e outPipeC || \ -e outPipeD ]]; then echo 'error' exit 1 fi # パイプの生成 mknod inPipeB p mknod inPipeC p mknod inPipeD p mknod outPipeB p mknod outPipeC p mknod outPipeD p # フィルタの生成 grep 'B' <inPipeB >outPipeB & grep 'C' <inPipeC >outPipeC & grep 'D' <inPipeD >outPipeD & # グループ分けの処理(メイン処理) cat data.txt | tee inPipeB inPipeC inPipeD | grep 'A' | cat - outPipeB outPipeC outPipeD # パイプの削除 rm inPipeB inPipeC inPipeD outPipeB outPipeC outPipeD |
処理の内容は最初にパイプの作成の前に利用するパイプと同名のファイルが存在しないことを確認します。
その後、パイプを作成してフィルタをバックグラウンドで実行させます。
次に、メイン処理として入力をteeコマンドを利用してそれぞれのパイプと標準出力に同じ出力を流します。
その後の処理は、残っているグループAの行を取り出して、最後のcatコマンドで処理したグループAとパイプからのそれぞれの出力を結合します。
これでメイン処理は終わります。
最後の後処理として、作成したパイプを削除します。そして、スクリプトの終了と同時に、このスクリプトの子プロセスでバックグラウンド処理中のフィルタも破棄されます。
実行結果は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
$ ./script.sh M,25,A,432 F,28,A,567 M,35,A,456 M,45,B,234 M,50,B,321 F,32,C,786 F,22,C,890 F,48,C,789 M,40,D,123 F,20,D,678 |
フィルタをawkコマンド等で工夫すると集計ツールのようなことも一応できます。もしよかったら頭の体操に試してみてください。