めもぶろぐ

お勉強したこと、趣味なんかを適当に書いてます。。。

bash-completionのカスタマイズ・自作

bash-completionってなんて便利なんだろう。。。
シェルスクリプトを作成していて、引数をよく使うのですが、どれ使えばいいんじゃい
となってしまったときタブ補完できたらなあ



なんて思い、bash-completionを調べました。
とりあえずどこに有るかは、下記コマンドを実行してみてください。


updatedb && locate bash-completion

#
# [実行結果] 1行抜粋
# /usr/share/bash-completion/completions/vgextend
#


ということで、/usr/share/bash-completion/completionsにありました。



addpartというやつが一番単純そうなので、これを参考に自作補完機能を実装します。
ログインプロファイルにでも自作補完機能を配置しましょう。


_addpart_module()
{
	local cur prev
	COMPREPLY=()
	cur="${COMP_WORDS[COMP_CWORD]}"
	case $COMP_CWORD in
		1)
			local DEVS=''
			while read dev; do DEVS+="$dev " ; done < <(lsblk -pnro name)
			OPTS="--help --version $DEVS"
			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
			;;
		2)
			# FIXME: how to determine next free partition number
			;;
		3)
			COMPREPLY=( $(compgen -W "start" -- $cur) )
			;;
		4)
			COMPREPLY=( $(compgen -W "length" -- $cur) )
			;;
	esac
	return 0
}
complete -F _addpart_module addpart

addpartは上記のような感じ。
配列に引数を格納していってそれを表示させる感じです。



試しに自作completionとシェルを作ってみます。



/var/log/message向けに時間を絞り込んでログを出力するシェルをつくります。

CentOS7から、もしくは他のディストリビューションでは
journalctl --sice=12:00 とかで対応できるので、全く意味のないシェル。





お試しシェル1号

#!/bin/bash

argv=()
while ([ ${#} -ne 0 ]); do
  case "${1}" in
    --*)
      if [[ "${1}" =~ "start=" ]]; then sTime=$(echo "${1}" | cut -d= -f2); fi
      if [[ "${1}" =~ "end=" ]];   then eTime=$(echo "${1}" | cut -d= -f2); fi
      shift
      ;;
    *)
      argv=("${argv[@]}" "${1}")
      shift
      ;;
  esac
done

awk -v sTime=${sTime} -v eTime=${eTime} '{
  if (sTime <= $3 && $3 <= eTime) {
    print
  }
}' ${argv[0]}

この時点では当然タブで補完ができない。
実行するときは下記のような指定方法が必要。


なお、引数は順不同。



/root/work/ExtractLog.sh /var/log/messages --start=22:00 --end=22:10


で、ここでの問題点は引数に--startやら--endやらが必要であること。
引数のオプションなんて忘れるし、順番も忘れる。




まあ、--foo --barって指定できると便利なことも有るのだけど。




補完の設定をしましょう。
.bash_profileに追記しましょう。
もしくは/etc/profile.d/user_bash_completion.shとかいう感じで新規作成もよし。




タブ補完向けの関数作成

_exlog_complete() {
  local cur
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"

  ORG=$IFS
  IFS="
  "
  OPTS=("--start=" "--end=")
  COMPREPLY=( $(compgen -W '${OPTS[@]}' -- $cur) )

  IFS=$ORG
  return 0
}
complete -F _exlog_complete -o default ExtractLog.sh

"complete -F 関数名 -o default 補完機能を実行するときの対象になるコマンド"


"-o default" はファイルとかディレクトリを補完時に出力してくれます。
ファイル・ディレクトリ名の補完が不要のときはつけなくて良いです。




今回はログファイル名の指定をするようにしているので、オプションを入れています。





末尾のコマンド名はシェル名でも良いです。なので下記のようにして
複数シェルにたいして補完機能が効くようにしても便利です。



Command=$(find /root/work -type f -name "*.sh" -exec basename {} \;)
complete -F _exlog_complete -o default ${Command[@]}


できあがったので、ログインし直してシェル実行してみます。

#
# まずはシェル名を指定する
#

[root@localhost work]$./ExtractLog.sh

#
# タブを押して見る。
#

[root@localhost work]$./ExtractLog.sh --
--end=    --start=

#
# おお、共通部分まで保管してくれた。
# eを押してタブを押して見る。
#

[root@localhost work]$./ExtractLog.sh --end=


できた。。。

おわり

広告を非表示にする