名付けてgrepeco。
peco/peco: Simplistic interactive filtering tool
色んなデータを柔軟に絞り込みしたい
各種のデータをパイプで処理するとき、適当にワードで絞り込んでからpecoでインクリメンタルサーチしたいことがよくある。これを簡単にできるようにしたい。
想定される用途はこんな感じ。
$ ls | grepeco hoge
パイプで与えられたデータを、ターミナルから入力された文字列で絞り込む。
パイプからの入力とターミナルからの入力を区別する
ここで、パイプで与えられたデータとはシェルスクリプトの標準入力、ターミナルから入力された文字列とは引数である。
シェルスクリプト内において、この二つは以下のように得ることができる。
# 標準入力を得る
cat -
# 引数を得る
echo "$1" # 1つめ
echo "$*" # すべて(1つの文字列として)
for i in "$@" # すべて(配列として)
do
echo "arg: $i"
done
この場合、引数を配列として扱うのであれば、複数の検索語による、複数の検索結果からさらに絞り込むことができる。
コード
以上を踏まえて、コードは以下のようになる。
grepeco.sh
#!/bin/bash
# 引数を単一の文字列として扱う
# 変数を初期化
pipes=()
prompt="select stdin from pipe"
optm=""
usage () {
echo "Usage: grepeco [-1h] [-p prompt] <args>" 1>&2
echo " -1: find only one" 1>&2
echo " -p: change peco prompt" 1>&2
echo " -h: view help" 1>&2
}
while getopts "1p:h" OPT
do
case $OPT in
1)
optm="m1" # grepのオプションを変更
;;
p)
prompt="$OPTARG" # pecoのプロンプトを変更
;;
h)
usage
exit 0
;;
?)
echo "ERROR: Undifined option" 1>&2
usage
exit 1
;;
esac
done
shift $(($OPTIND - 1))
if [ -p /dev/stdin ];then # パイプ入力があるか判断
while read pipe
do
pipes+=("$pipe")
done < <(if [ -n "$1" ]; then # 引数があるか判断
cat - | grep -i"$optm" "$*"
else
cat -
fi)
else
echo "ERROR: No stdin from pipe" 1>&2
exit 1
fi
case "${#pipes[@]}" in # pipesの要素数による分岐
0 )
echo "ERROR: No hit." 1>&2
exit 1
;;
1)
echo "${pipes[*]}"
;;
*)
for i in "${pipes[@]}"
do
echo "$i"
done | peco --prompt "$prompt"
;;
esac
grepeco.sh
#!/bin/bash
# 引数を配列として扱う
- done < <(if [ -n "$1" ]; then
- cat - | grep -i"$optm" "$*"
- else
- cat -
- fi)
+ done < <(if [ -n "$1" ]; then
+ stdin="$(cat -)" # ループで標準出力がリセットされるため変数に格納
+ for arg in "$@"
+ do
+ echo "$stdin" | grep -i "$arg"
+ done | sort -u # 重複する結果を削除
+ else
+ cat -
+ fi)
if文の結果をwhile文に渡すということを思いついてコードがシンプルになった。