コマンドの包み方
素敵なラッピングでプレゼントはより魅力的な贈り物になる。コマンドもラッピングすることで、使い勝手や価値が向上するかもしれない。
例:growlnotifyコマンド
- OSX10.4からOSX10.5にアップグレードした時、Growlを通知するコマンドgrowlnotifyが一時使えなくなってしまったことがあった。
- しかし、Growlにはネットワーク経由でメッセージを送る機能もあり、その機能だけはgrowlnotifyで唯一利用できた。
# -H コンピュータ名.localで送信先のマシンを指定する(システム環境設定 >> 共有で確認できる) $ growlnotify -H PowerBookG4.local -m test_message # あるいは、-H IPアドレスを指定してもOK $ growlnotify -H 10.0.1.4 -m test_message # 自分自身のメッセージもネットワーク経由で送信できる(もちろん、名前やIPアドレス指定でもOK) $ growlnotify -H localhost -m test_message
- それなら、自分宛のメッセージもすべてネットワーク経由にしてしまおうという対策が公開されたことがあった。
- Growlnotify Leopard Incompatibility Workaround : hasseg.org
#!/bin/bash # growlnotify leopard bug workaround # # シェルスクリプトの関数は、()の中に引数を持たない、 # コマンドと同じように、$@や$1, $2, ...等のグローバル変数で参照する list_args() { for p in "$@" do # STR="0123456" # echo "${STR:2:3}" # 2文字目から、3文字分(長さ) # => 234 # echo "${STR:2}" # 2文字目から、すべて # => 23456 if [ "${p:0:1}" == "-" ];then # 先頭が"-"である場合 echo -n "$p " # 改行しないで出力する else echo -n "\"$p\" " # ダブルクォートして、改行しないで出力する fi done } # "${@:$?}"___引数$@がない場合、直前に実行したコマンドの終了コードを返す # $(コマンドあるいは関数)___コマンドあるいは関数を実行した結果を返す # argstr=`list_args "${@:$?}"` と同等 argstr=$(list_args "${@:$?}") # xargsはパイプで渡されたデータを、指定してたコマンドの引数にして実行する echo "-H localhost $argstr" | xargs /usr/local/bin/growlnotify.wrapped # ---------- 特殊な変数の意味 ---------- # $$ シェル自身のPID(プロセスID) # $! シェルが最後に実行したバックグラウンドプロセスのPID # $? 最後に実行したコマンドの終了コード(戻り値) # $- setコマンドを使って設定したフラグの一覧 # $* 全引数リスト。"$*"のように「"」で囲んだ場合、"$1 $2 … $n" と全引数を一つにくっついた形で展開される。 # $@ 全引数リスト。"$@"のように「"」で囲んだ場合、"$1" "$2" … "$n" とそれぞれの引数を個別にダブルクォートで囲んで展開される。 # $# シェルに与えられた引数の個数 # $0 シェル自身のファイル名 # $1〜$n シェルに与えられた引数の値。$1は第1引数、$2は第2引数…となる。
- オリジナルのコマンドは、growlnotify.wrappedに変更した。
- 上記スクリプトをgrowlnotifyで保存して、実行権限を追加した。
これでようやく、OSX10.5でもgrowlnotifyが利用できるようになったのである。
aliasコマンドを活用する
- しかし、今思えば、上記の長い長いgrowlnotifyをラップするスクリプトは、以下の1行でも良かった気がする。
$ alias growlnotify='growlnotify -H localhost'
- alias コマンド名='置き換える文字列'
- aliasで指定したコマンド名は、置き換える文字列と解釈される。(その後にすべての引数が続く)
- 修正したい場合は、aliasコマンドで再度設定すればOK。
- オリジナルなgrowlnotifyコマンドを実行したい時は、以下のようにすればOK。
$ /usr/local/bin/growlnotify -m test_message # パスで指定する $ command growlnotify -m test_message # commandで実行する $ \growlnotify -m test_message # 先頭に\(半角バックスラッシュ)を付ける()
- エイリアスの一覧は、aliasコマンドを引数なしで実行すれば表示された。
$ alias alias cot='open -a CotEditor' alias growlnotify='growlnotify -H localhost' alias rm='rm -i'
- エイリアス設定を削除したい場合は、unaliasコマンドで削除された。
$ unalias growlnotify $ alias alias cot='open -a CotEditor' alias rm='rm -i'
例:afplayコマンド
前回まで延々とNSSoundを利用したsoundコマンドなるものを実装してきたが、実は車輪の再発明であり、既に同等の機能を持つafplayコマンドが存在した。しかし、唯一、soundコマンドにはあって、afplayコマンドにはない機能として、警告音の名称による再生がある。
- sound BlowはOKだが、afplay Blowではエラーになってしまう...。
- afplay /System/Library/Sounds/Blow.aiffとパスを指定する必要があるのだ。
こんなとき、afplayコマンドをラッピングするコマンドを作って、NSSound仕様のファイル名検索機能を追加してしまえば、手軽に欲しい機能を追加できる可能性があるのだ。
- 警告音の名称を、事前にパスに変換してからafplayに渡すようにしている。
#!/bin/bash array=($@) search_path=`echo echo {~,,/Network,/System}/Library/Sounds` sound_name=`echo ${array[$(($# - 1))]}` sound_file=`find $search_path -name "${sound_name}.*" 2>/dev/null | head -n 1` if [[ $sound_file != '' ]]; then array[$(($# - 1))]=$sound_file fi afplay ${array[@]}
関数の利用
- そこで、以下のようにbashの関数として定義にしておくと、幸せを感じた。
afplay(){ array=($@) search_path=`echo echo {~,,/Network,/System}/Library/Sounds` sound_name=`echo ${array[$(($# - 1))]}` sound_file=`find $search_path -name "${sound_name}.*" 2>/dev/null | head -n 1` if [[ $sound_file != '' ]]; then array[$(($# - 1))]=$sound_file fi command afplay ${array[@]} }
- 但し、関数を有効にするためには、以下のように . ドットコマンドで読み込んでおく必要がある。(ファイルを~/afplay.shとして保存した場合)
$ . ~/afplay.sh
- 関数は、シェルを起動する度に読み込む必要がある。
- 手入力は面倒なので、.bash_profile・.bashrcなどの設定ファイルで. ~/afplay.shを実行するようにすれば、いつでも利用可能になる。
- setコマンドで、シェル変数と関数の一覧が表示された。
- printenvコマンド、あるいはexportコマンドで環境変数の一覧が表示された。
- ちなみに、Developer Toolsをインストールすると、以下のサンプルコードの中にafplayなるコマンドが見つかる。
- /Developer/Examples/CoreAudio/Services/AudioFileTools/
- オプションの指定方法が異なっているが、似ているところもあり、afplayコマンドはアップルが提供しているのかもしれない。