Help us understand the problem. What is going on with this article?

grepとpecoによる順次絞り込みするシェルスクリプト

More than 3 years have passed since last update.

名付けて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文に渡すということを思いついてコードがシンプルになった。

参考

catfist
物書き志望です。
https://wbtmiu.herokuapp.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away