低レベルインターフェースと高レベルインターフェース
- ref:J
結局インターフェースの問題なのでは。
単機能のコマンドを組み合わせて、複雑な処理を組み合わせるってのは組み合わせ方次第で、いろんなことが出来るようになるという点で優れてはいるんだけど、そりゃ毎回おんなじことやっていりゃ面倒だし、1つのコマンドで実行できたほうが簡単ではある。gzip/bzip2 と tar だって、みんな GNU tar の場合 -z オプションとか -j オプションとか使うよね(最近は GNU tar はファイルの中身をみて自動認識するからつけなくても展開できるけど)。いちいち明示的にパイプしない。まぁ、そもそもあれが GNU tar の独自拡張オプションだということ自体知らない人が多いけど(そんでもって GNU tar じゃない環境にいくとたちまちアーカイブを展開できなくなる)。
でだ。実は、悪いのは組み合わせないことじゃなくて、組み合わせにくいことなんじゃないだろうか。美しさとかそういう問題じゃなくて。
たとえば、grep が「指定ディレクトリ以下のファイルから特定のパターンを検索する」コマンドなら検索ファイルの条件を指定したい場合は grep を拡張するしかないわけだけど、そうじゃないから find と組み合わせて複雑なことが出来るわけだ。要するに再利用性の問題。
同じことはプログラミングにもいえて、いきなりモノリシックに機能を1つの関数に作りこむんじゃなくて、単純な機能を複数作って組み合わせて高レベルインターフェースを構築したほうが再利用性は高いし、柔軟性も高い。
で、grep と find の問題は適当に Facade 的なものを用意すれば使いやすくなっていいんじゃね、とか思ったので、ちょっと作ってみた。
#!/bin/bash function usage { help_message="Usage: $0 path [grep_args] [-- [find_args]]" if [ "$1" -eq 0 ]; then echo "$help_message" else echo "$help_message" 1>&2 fi exit "$1" } if [ $# -eq 0 ]; then usage 2 elif [ "$1" = "--help" -o "$1" = "-h" ]; then usage 0 fi declare -a grep_args declare -a find_args path="$1"; shift while [ $# -gt 0 ]; do arg="$1"; shift if [ "$arg" = "--" ]; then break else grep_args[${#grep_args[@]}]="$arg" fi done if [ ${#grep_args[@]} -eq 0 ]; then # grep args not found usage 2 fi while [ $# -gt 0 ]; do arg="$1"; shift find_args[${#find_args[@]}]="$arg" done find "$path" -type d \( -name .svn -o -name CVS -o -name RCS \ -o -name _darcs -o -name blib \) -prune \ -o -type f "${find_args[@]}" -print | perl -ne 'chomp; print $_, "\0" if -T' | \ xargs -0 -e grep "${grep_args[@]}" -- /dev/null
こんな感じで使う。
grep-find . background-image -- -name "*.css"