bashの各種のコマンドを、簡単に編集できるようにしたい。
環境はMac。
コマンドの種類
bashで利用できるコマンドには、以下の5種類がある。
- エイリアス
- 関数
- ファイル
- 予約語
- シェル組み込みコマンド
このうち、自分で作成できるのは1〜3である。これらを一括して編集できるのが理想。
typeコマンドによるコマンドの判別
コマンドが1〜5のどれであるかは、typeコマンドによって判断できる。
# -tオプションでタイプのみを表示
$ type somealias # エイリアス
somealias is aliased to `echo "somealias"'
$ type -t somealias
alias
$ type somefunc # 関数
somefunc is a function
somefunc ()
{
echo "somefunc"
}
$ type -t somefunc
function
$ type ls # ファイル
ls is /bin/ls
$ type ls # ハッシュされたファイル
ls is hashed (/bin/ls)
$ type -t ls
file
$ type -p ls # パスのみを出力。whichだとパスが通っていても出ない場合がある
/bin/ls
$ type for # 予約語
for is a shell keyword
$ type -t for
keyword
$ type type # 組み込みコマンド
type is a shell builtin
$ type -t type
builtin
ファイルをを何度か実行するとログイン中のシェルでハッシュされる。ハッシュされたものはファイルである。
ファイルがバイナリであるか確認
ファイルがバイナリである場合、エディタで直接編集することはできない。よって、コマンドがバイナリファイルであった場合、除外する必要がある。
あるファイルがバイナリであるかどうかは、fileコマンドで判別する。
# -bオプションで属性のみを表示
$ file -b someprog # テキストファイル(編集可能)
Bourne-Again shell script text executable
$ file -b $(type -p ls) # 'text'を含まないものはバイナリ(編集不可)
Mach-O 64-bit executable x86_64
関数化
typeコマンドの結果を取得し、それに応じて処理するシェル関数を作成する。
このスクリプト自体は、ファイルではなく関数でなければならない。
イリアスと関数の変更を反映するためにsource
コマンドを実行するが、ファイルから実行すると、source
が別のプロセスで実行されてしまい、カレントシェルに変更が反映されないため。
編集は、VimまたはMacVimで行う。
- エイリアスまたは関数の場合、編集終了を関知してsourceするのを容易にするため、Vimを使う。
- プログラムの場合、編集と実行を平行して行えるよう、MacVimを使う。
- 色つきで出力するのにGNU sedを使用。
- 機能を増やしすぎた。
# in ~/.bashrc
# export bash_alias=~/.bash/conf/alias-init.bash
# export bash_function=~/.bash/conf/function-init.bash
# export bindir=~/shellsctipts
# export PATH=$PATH:$bindir:./bin
# source "$bash_alias"
# source "$bash_function"
ec () { #edit shell commands
local dflag=0
local pflag=0
local OPTIND_OLD="$OPTIND"
local OPTIND=1
usage () {
echo "Usage: ec [-dph] <comand name>" 1>&2
echo "-d: delete command." 1>&2
echo "-p: print aliased to/function or 'what' is file, and copy code." 1>&2
echo "-h: print help." 1>&2
}
while getopts dph OPT
do
case $OPT in
d)
dflag=1
;;
p)
pflag=1
;;
h)
usage
return 0
;;
?)
usage
return 1
;;
esac
done
shift $((OPTIND - 1))
OPTIND="$OPTIND_OLD"
if [ -z "$1" ]; then
echo "ERROR: input command name to edit" 1>&2
return 1
fi
if ! type "$1" >&/dev/null; then
echo "ERROR: Undifined command." 1>&2
return 1
fi
case "$(type -t "$1")" in
"function" )
if [ "$pflag" = 1 ]; then
type "$1" | sed -e 1d -e 's/;$//g' | tee >(pbcopy)
return 0
fi
if [ "$dflag" = 0 ]; then
vim "$bash_function" +"/^$1 ()"
else
echo -e "function deleted: \033[032;m$1\033[0m" 1>&2
sed -i '' "/^$1 () {/,/^}/d" "$bash_function" 1>&2
fi
source ~/.bashrc
;;
"alias" )
if [ "$pflag" = 1 ]; then
type "$1" | sed -e 's/.* to `//' -e "s/'$// | tee >(pbcopy)" 1>&2
return 0
fi
if [ "$dflag" = 0 ]; then
vim "$bash_alias" +"/^alias $1="
else
gsed -n "s/^alias \($1=.*\)/alias deleted: \x1b[33;m\1\x1b[0m/p" "$bash_alias" 1>&2
gsed -i "/^alias $1=/d" "$bash_alias"
fi
source ~/.bashrc
;;
"file")
local cmdpath="$(type -p "$1")"
if file -b "$cmdpath" | grep -q 'text'; then
if [ "$pflag" = 1 ]; then
what "$cmdpath"
pbcopy < "$cmdpath"
return 0
fi
if [ "$dflag" = 1 ]; then
echo -n "rm: "; rm -v "$(readlink "$cmdpath")"
echo -n "rm: "; rm -v "$cmdpath"
return 0
fi
mvim "$cmdpath"
else
echo "Not text/script file" 1>&2
return 1
fi
;;
"keyword"|"builtin")
echo "$1 is shell keyword or builtin." 1>&2
return 1
;;
* )
echo "Unexpected error." 1>&2
type "$1"
return 1
;;
esac
}
参考
- Bashでコマンドの存在チェックはwhichよりhashの方が良いかも→いやtypeが最強 - Qiita
- hashコマンドについて - FreeBSD初心者運用日記 - freebsdグループ
- Emacs - 設定ファイルを複数ファイルに分割して管理する構文のまとめ - Qiita