パイプラインの経路を動的に切り替える(シェルスクリスプト)

課題

コマンドラインのオプション指定を受けて処理内容を切り替えたい。対象の処理がパイプライン中のあるコマンドであるとき、どのようにすれば良いか。ほとんど処理が同じパイプラインを2つ用意するのはイケテいない。

解決策

対象の処理をif文にはめ込む。何も行わないケースはcatでバイパスする。

イメージとしては、if文のそれぞれのケースに対応するコマンドが、if文へのデータ入力(標準入力)を奪い取り、自身の処理を行なって出力する。注意するべきは、何も処理を行わないケースのケアをしなければならないことだ。スクリプトとして何もケアしなければパイプラインの経路が切断されてしまう。

具体的な例を挙げて説明しよう。0から9までの整数列が入力されてきたとする。これらの値を加工することでどのように処理が切り替えられているか確認する。

$ seq 0 9
0
1
2
3
4
5
6
7
8
9

これらの値に対して乗算→加算する場合と、加算のみする場合で切り替えてみる。計算自体はawkを利用すれば簡単であるがどのように実装できるか。

前述の説明の通り、if文を利用して記述してみる。ここでは2の乗算と1の加算を行っている。

is_double=yes

seq 0 9                       |

if [ "${is_double}" = 'yes' ]; then
  awk '{ print $1 * 2; }'
fi                            |

awk '{ print $1 + 1; }'

これを実行すると以下の表示がある(入力の値が2倍され1加算された)。意図通りに動いた!完璧!

1
3
5
7
9
11
13
15
17
19

………残念ながら完璧というのは半分嘘だ。ifの条件式をfalseとした場合の動作も確認してみよう。以下のように例えば is_double に no の値をセットするとそのような状況になる。これを実行すると端末には何も表示されない。

is_double=no

seq 0 9                       |

if [ "${is_double}" = 'yes' ]; then
  awk '{ print $1 * 2; }'
fi                            |

awk '{ print $1 + 1; }'

理由は単純で、if に入力されたデータを出力する役割を担ったコマンドが存在しないからだ。これをケアしておかなければならない。falseの場合の経路をバイパスしてくれるコマンドをおいておけばよい。

is_double=no

seq 0 9                       |

if [ "${is_double}" = 'yes' ]; then
  awk '{ print $1 * 2; }'
else
  cat
fi                            |

awk '{ print $1 + 1; }'

これを実行すると falseの場合でも意図通りの計算結果が出力された(入力の値が1加算された)。

1
2
3
4
5
6
7
8
9
10

所感

シェルスクリプトのオプション指定により処理を切り替えたい状況は往々にして存在する。その中の一部ではこのようなif文による実装が役に立つ。回路をスイッチングするみたいで面白い(よね?)