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

オレオレgrep

grepコマンドが優しくない

ファイルの中の文字列を検索する「grep」コマンド。
使用頻度がかなり高いとは思いますけど、どうにも優しくない。
優しくなさすぎて「find」コマンドと併用したりしてしまいがち。
「find」は、それはそれでコマンド行が長くなりがちなので、ここはひとつシェルスクリプトで補完してみようじゃないかと思いたちました。

まずは、デフォルト状態を決めます。

  • ヒットしなかった行は表示しない
  • 標準でカラーモード
  • ヒットした行番号を表示する
  • 指定ディレクトリのファイルすべてが対象
  • でもファイルの拡張子は指定したい

というわけで実装してみます。

オプション処理

bashでのオプション処理と言えば「getopts」ですが、欠点としてよく挙げられているのが「オプション以外の引数の後ろにオプションを指定出来ない」というものがあります。
しかし、getoptsが無効なオプションが出てきたらそこで処理を止めているだけで、オプション引数にはちゃんと格納されています。
なので、

  1. getoptsでループを廻しオプション処理をする
  2. ループを抜けたら処理した引数の数(オプションの数ではない)だけ「shift」する
    ただし、オプション以外の引数の前にオプション指定がなかった場合はこの数が「0」なので、ひとつだけ「shift」させる
  3. 「$1」を引数として取り出す
  4. 「$1」が空だった場合はオプション処理終了

という形にすると、オプションとオプション以外の引数を混在させられます。

対象ディレクトリ

grepする場合は、大抵がディレクトリ内の全ファイルが対象なので、ファイルの指定は基本的には「」(ワイルドカード)にする。
ディレクトリ指定がなかった場合はカレントディレクトリの「
」、ディレクトリが指定されていた場合はそのディレクトリの「*」を対象にします。しかし、ファイルの種類だけでも指定出来ると便利なので「拡張子」を指定出来るようにしました。

スクリプト

そして出来たのがこちらになります。

#!/usr/bin/env bash

CMD=`basename $0`

#=============================================================================
# error handler
# エラー処理
#=============================================================================
function error() {
  case ${1} in
    [12] )
      echo "Usage:"
      echo "$CMD [-r] [-e ext] [other grep option] search_string [search_directory]"
      echo "-r    : Recursive"
      echo "-e ext: Limits the search to files with the specified extension."
      echo ""
      ;;
    [34] )
      echo "require option parameter."
      echo ""
      ;;
    [5] )
      echo "invalid option - '${2}'"
      echo ""
      ;;
  esac
  exit
}

#=============================================================================
# option procedure
# オプション制御
#=============================================================================
function optproc() {
  local OPT OPTARG
  OPT=$1
  OPTARG=$2

  case $OPT in
    [e] )
      if [ -z "$OPTARG" ] || [ ${OPTARG:0:1} = "-" ]; then
        error 3
      else
        OPTION="$OPTION --include *.$OPTARG"
      fi
      ;;
    [r] )
      RECURSIVE="-R"
      TARGET=""
      ;;
    [C] )
      if [ -z "$OPTARG" ] || [ ${OPTARG:0:1} = "-" ]; then
        error 4
      else
        OPTION="$OPTION -C$OPTARG"
      fi
      ;;
    [\?] )
      error 5 $OPTARG
      ;;
    [:] )
      ;;
    * )
      OPTION="$OPTION -$OPT"
      ;;
  esac
}

#=============================================================================
# main procedure
# メイン処理
#=============================================================================

#---------------------------
# exec without option
# オプションなしで実行
#---------------------------
if [ -z "${1}" ]; then
  error 1
fi

#---------------------------
# variable
# 変数
#---------------------------
COMMAND="/usr/bin/env grep"
OPTION="--color=auto -Isn"
RECURSIVE=""
TARGET="*"
SEARCH=""

#---------------------------
# option loop
# オプションでループ
#---------------------------
arg="-"
argarr=()

#---------------------------
# valid option
# 有効なオプション
#---------------------------
option=":lvire:C:VcLo"
IFS=$'\n'

while [ ! -z $arg ]; do
  while getopts $option OPT; do
    optproc $OPT $OPTARG
  done

  num="$(($OPTIND-1))"
  shift $num
  arg=$1

  if [ ! -z "$arg" ]; then
    argarr+=($arg)
    OPTIND=1
    shift
  fi

done

SEARCH=${argarr[0]}
if [ -z $SEARCH ]; then
  error 4
fi
num=${#argarr[*]}
DIRTMP=""
for i in $(seq $num); do
  argstr=${argarr[$i]}
  if [ ! -z "$argstr" ]; then
    DIRTMP="$DIRTMP $argstr"
  fi
done

IFS=$'  '

if [ ! -z "$DIRTMP" ]; then
  DIRECTORY=${DIRTMP%/}
  TARGET="$DIRECTORY/$TARGET"
fi

$COMMAND $SEARCH $RECURSIVE $OPTION $TARGET

結構、汎用的には作ってあるので、シェルスクリプトの雛形としてお使いください。

digitarhythm
いろいろ作っています。 フルスタックフレームワーク「ViewllerJS」 https://www.digitarhythm.net/viewllerjs ブラウザでWebゲーム開発「enforceIDE」 https://www.prominence.tv/enforceIDE WebブラウザでProcessing「InstaProc」 https://processing.fun/
http://www.digitarhythm.net
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