zsh Advent Calendar 2013 兼 拡張 POSIX シェルスクリプト Advent Calendar 2013、9日目の記事です。
今日は既存のシェルスクリプトを書き換えることなく動作を変える方法を紹介します。 当初は zsh 向けの記事にしようと思っていたのですが、 一部限定的ながら bash, ksh にも適用できることに気付いたため、 最初は bash での例を示し、最後に zsh ならではの特徴を挙げます。
今回は例として bash スクリプトである xzgrep を uuencode に対応してみます。
xzgrep は xz だけでなく、gzip, bzip2, lzo
で圧縮されたファイルを指定すると自動的に展開し、その出力に対して grep
を実行するコマンドです。
$ xzgrep pattern msg.txt.xz
… `xz -dc msg.txt.xz |grep pattern` 相当の出力…
材料を用意する
xzgrep と uuencode, uudecode コマンドが含まれているパッケージをインストールします。
RHEL 等の場合
# yum install xz sharutils
Debian 等の場合
# apt-get install xz-utils sharutils
…
$ file /usr/bin/xzgrep
/usr/bin/xzgrep: Bourne-Again shell script, ASCII text executable
xzgrep 対象のテキストファイル msg.txt と、
xz で圧縮した msg.txt.xz、uuencode でエンコードした msg.txt.uu
を作成します。
$ (echo しにたい; echo もうだめだ; echo つらぽよ) >msg.txt
$ xz <msg.txt >msg.txt.gz
$ uuencode msg.txt <msg.txt >msg.txt.uu
$ cat msg.txt.uu
begin 664 msg.txt
JXX&7XX&KXX&?XX&$"N."@N.!AN.!H.."@>.!H`KC@:3C@HGC@;WC@H@*
`
end
xzgrep の動作を確認します。
msg.txt.uu に対しては uuencode 形式には対応していないため、何も表示されません。
$ xzgrep だめ msg.txt*
msg.txt:もうだめだ
msg.txt.xz:もうだめだ
xzgrep を uuencode 対応してみる
こんなスクリプトを別途用意します。
これを実行してみましょう。
$ bash ./xzuugrep.bash だめ msg.txt*
msg.txt:もうだめだ
msg.txt.uu:もうだめだ
msg.txt.xz:もうだめだ
このように、間接的に xzgrep を呼び出す形ではありますが、
無改造で uuencode 形式対応することができました。
解説
bash が環境変数 $BASH_ENV
に指定されたファイル内のスクリプトを起動時に実行するという挙動を利用し、
本来のスクリプト実行前にシェル関数でコマンドを上書きしただけの簡単なハックです。
xzuugrep.bash スクリプトの動作を簡単に解説します。
- bash が
xzuugrep.bashスクリプトを開始する。 $XZUUGREPが未定義であるため、次の処理を行なう:- 環境変数
$XZUUGREPを設定する。(値は何でもよい) - 環境変数
$BASH_ENVに自スクリプト名 (xzuugrep.bash) を設定する。 xzgrepを起動する。(execなので戻ってこない)
- 環境変数
xzgrepの shebang が#!/bin/bashであるため、bash が起動する。- 環境変数
$BASH_ENVが設定されているため、 bash はその値が示すxzuubash.bashスクリプトを実行する。 (source xzuubash.bash相当) 結果、シェル関数xz()が定義される。 - bash は続いて
xzgrepスクリプトを実行する。 xzgrepの実行中、通常時xzコマンドを実行される代わりにxz()が実行される。xz()は指定されたファイルから最初の一行を抽出し、 それが uuencode 形式と認識したらuudecodeコマンドでデコード、 そうでなければxzコマンドで展開する。
xzuugrep.bash からの xzgrep 実行は exec xzgrep でなく
source xzgrep でもよいのですが、その場合、xzgrep 内で $0 の値が
xzgrep ではなく xzuugrep.bash になります。
$0 の値に依存しているスクリプトではうまく動作しないでしょう。
ksh の場合
ksh は起動時に環境変数 $ENV を参照しますが、
シェルスクリプト(非対話)の場合は無視されます。
よって今回のハックは不可能です。
代わりに zsh の ksh エミュレーションを利用しましょう。
zsh の場合
zsh は起動時の名前が ksh あるいは sh の場合に環境変数 $ENV を参照します。
上記ハックを参考に $BASH_ENV の代わりに $ENV を利用し、
本来のスクリプトの起動は exec -a /bin/sh /bin/zsh <スクリプト名> <引数>
のように実装すれば同様のハックが実現できます。
bash の場合はパス名を含むコマンドの上書きはできませんが、
zsh の場合はシェル関数やコマンドエイリアスの名前に /
を含めることができるため、パス名を含むコマンドも上書き可能です。
もし仮に xzgrep スクリプト内で xz コマンドが /usr/bin/xz
のように絶対パスで指定されていると bash では対応できませんが、
zsh なら対応できます。
ユースケース
色々考えられると思いますが、私の場合は Linux 向けに作成されたシェルスクリプトを Linux 以外 (具体的には Solaris、AIX) で動作させるため、 Linux 互換のコマンドとこのハックを組み合わせて利用しています。 そうすることで一つのスクリプトで複数の OS に対応しています。
ところで、12月25日はクリスマスな上に、
OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
っ http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority
逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!
私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/
よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。
- Ansible Advent Calendar 2023
- シェル芸 Advent Calendar 2023
- 闇の魔術に対する防衛術 Advent Calendar 2023
- Ansible Advent Calendar 2023
- Ansible Advent Calendar 2020
- DNS温泉 Advent Calendar 2019
- OSSTech Advent Calendar 2019
- Ansible Advent Calendar 2018
- OSSTech Advent Calendar 2018
- Debian/Ubuntu Advent Calendar 2017
- Linux Advent Calendar 2017
- Shell Script Advent Calendar 2017
- Shell Script Advent Calendar 2016
- OpenLDAP と仲間たち Advent Calendar 2015
- Postfix Advent Calendar 2014
- 拡張 POSIX シェルスクリプト Advent Calendar 2013