なるべく労力をかけずにサブコマンド付きのシェルスクリプトを書きたい、ということがあった。
労力をかけないことに力点をおいたので、 POSIX 準拠ではなく、 Bash 限定の要素が入っている。
コマンドをそのまま実行するとヘルプ表示
$ some-command
Usage:
some-command --help
some-command sub-command1
some-command sub-command2
--help
オプションでもヘルプを表示
$ some-command --help
Usage:
some-command --help
some-command sub-command1
some-command sub-command2
sub-command1
, sub-command2
でそれぞれ違った処理を実行する。
※ 下記の例示はただの echo
$ some-command sub-command1
1
$ some-command sub-command2
2
存在しないサブコマンドにもヘルプ表示
また、エラー扱いとして exit code 1 で終了したい。
$ some-command wrong-command
Usage:
some-command --help
some-command sub-command1
some-command sub-command2
$ echo $?
1
どう書く
サブコマンドをシェルスクリプトの関数として対応関係にあれば、そこそこ可読性が高く、と追加や制御の手間が最小限になると考えた。
some-command
#!/usr/bin/env bash
sub-command1() {
echo 1
}
sub-command2() {
echo 2
}
--help() {
echo 'Usage:'
compgen -A function \
| xargs -I % echo ' ' "$(basename "$0")" %
}
main() {
unset -f -- "${FUNCNAME[0]}"
! declare -F -- "$1" >/dev/null && --help && exit 1
"$@"
}
main "$@"
テクニック解説
テクニック1. main をサブコマンドから消す
unset -f -- "${FUNCNAME[0]}"
${FUNCNAME[0]}
で自身の関数名が取得できるので、 unset すると名前空間から main が消えてくれる。後々の compgen -A function
でサブコマンドを得るときに main がない方が良い。
テクニック 2. 簡易ヘルプ
本来は入力補間を実現するのための組み込みコマンドとして compgen
というものがある。これをヘルプの表示につかう。具体的には、関数を列挙してサブコマンドとして表示する。
大本の実行コマンドは $(basename "$0")
から得る。
compgen -A function \
| xargs -I % echo ' ' "$(basename "$0")" %
テクニック 3. 関数名の存在チェック
declare -F
を使って関数名の存在チェックをしている。なければヘルプ表示
! declare -F -- "$1" >/dev/null && --help && exit 1
テクニック 4. "$@"
シェル関数を先頭で消費しつつ、残りを引数として関数に渡せる。
なので例えばもし sub-command1
を
sub-command1() {
echo "$@"
}
のような定義にすれば、
$ some-command sub-command1 A B C
A B C
となる。
サブコマンドで、実際の処理は他のコマンドに委譲する場合に便利になる。
まとめ
--help
から 最後の行まではボイラープレートとして捉えて、コピペすると楽でしょう。
ここはコピペ
# http://qiita.com/kitsuyui/items/4b204963e0ebec53fe3c
--help() {
echo 'Usage:'
compgen -A function \
| xargs -I % echo ' ' "$(basename "$0")" %
}
main() {
unset -f -- "${FUNCNAME[0]}"
! declare -F -- "$1" >/dev/null && --help && exit 1
"$@"
}
main "$@"