ls
なんかをalias ls='ls --color=auto'
とカラー表示をするようにaliasを作っていたりすることがあると思いますが、
この場合ls
と打つと必ずこの引数がついてきてしまいます。
また、ラッパー関数的なものを元のコマンドと同じ名前で作ろうとした時、 そのまま中で同じコマンドを使うと無限ループに陥ります。
そういった時に元のコマンドを使う方法について。
優先順位
シェルのコマンドとして実行されるものとして、
- alias
- 関数
- Bash等シェルのbuiltinコマンド(
echo
など) PATH
が通った所に置いてある実行ファイル(複数ある場合はPATHの中で先に指定してる場所のものが優先)
がありますが、これらが同じ名前で複数ある時、 上に書いた順で優先的に使われます。
例えばecho
というコマンドと同じ名前の関数やaliasを作った場合、
$ alias echo='printf "alias\n"'
$ function echo { printf ""func\n""; }
$ echo
alias
$
aliasを作った後に関数を定義しようとする時function
を使わないと定義できないのでちょっと注意。
type -a
で確認すると優先される順に全てを見ることが出来ます。
$ type -a echo
echo is aliased to `printf -- alias\n'
echo is a function
echo ()
{
printf "func\n"
}
echo is a shell builtin
echo is /bin/echo
$
これはmacOSでBashを使ってる時の出力ですが、 確かに上の順になっていることが分かります。
優先順位を無視して指定のコマンドを実行する方法
バックスラッシュでエスケープ
$ \command
の様にコマンド名の前にバックスラッシュを入れると aliasは無視されます。
ただし、この方法は関数は無視されません。
このバックスラッシュですが、行っているのは
- 最初の一文字をエスケープして文字列を解釈した後にコマンドを実行する
ということで、通常の文字ならエスケープしてもそのままなので 解釈後は同じ文字列になります。 この様にエスケープされたりした後のコマンドはaliasを無視する様になります。
実際バックスラッシュの位置はどこでもよくて、最初のecho
の例だと、
$ echo
alias
$ \echo
func
$ e\cho
func
と言った感じに途中にバックスラッシュを入れても同様です。(勿論最初と途中両方に入れても同じ。)
クォートする
これもバックスラッシュと同様ですが、 Bashなどではクォートされたりしているものを一度解釈してからコマンドを実行しますが、 コマンド自体がクォートされていてもコマンドは実行できます。
この様な場合にもaliasは無視されます。
$ "echo"
func
$ 'echo'
func
$
コマンドを変数に代入する
追記: 2018/08/18
ZshとBashの違いについて追記。
追記ここまで
さらにコマンド自体を変数にして実行することも出来ますが、 この際もaliasは無視されます。
$ a=echo
$ $a
func
command
command
というシェルのbuiltinコマンドを使うと
alias、関数の両方を無視してコマンドを実行する事が出来ます。
$ command echo
$
builtinのecho
か、/bin/echo
かこのままだと分からないですが、
Macの/bin/echo
では-e
オプションが無いのでこれでチェック。
Bashだと
$ command echo -e test
test
もし/bin/echo
だと-e test
の様に-e
がそのまま出力されるはずなので
ここではbuiltinなecho
が使われていることが分かります。
一方、Zshでやってみると
$ command echo -e test
-e test
となり/bin/echo
が呼ばれています。
従ってcommand
はBashとZshで動作が違ってきます。
動作としてはBashではbuiltin、実行ファイル、の順で探すのに対し、 Zshではbuiltinは無視して実行ファイルを探します。
LinuxだとRedHatとかなら/bin/echo
という実行ファイルがあり、
builtinのものだとecho --help
で--help
と表示されるだけですが、
/bin/echo --help
とするとechoのヘルプが表示されるので上のチェックを行うことが出来ると思います。
他にもcd
などが/usr/bin/cd
にあったりするので
command
を使う際にはBash/Zshの違いをきちんと理解して使う必要があります。
Bashであればcommand
を使うことで単にalias/関数を無視して通常のコマンドを使えますが、
Zshではbuiltinコマンドの素の状態を使いたい場合には下に書くbuiltin
を使う必要があります。
コマンドへのパスを書いて実行
$ /bin/echo
の様にコマンドファイルへのパスを指定すればそのファイルが実行されます。 builtinも無視したい場合にはこれ。
$ /bin/echo -e test
-e test
$
$(which command)
which
はbuiltinコマンドは返せないので
返ってくるのは実際にファイルのあるコマンドになります。
従ってこれを使えばbuiltinも無視して実行ファイルのコマンドを使うことが出来ます。
$ $(which echo) -e test
-e test
$
builtin
逆にbuiltinコマンドを使いたい時はbuiltin
というコマンドが使えます。
$ builtin echo -e test
test
$
alias X=X
alias
を使った同じ名前のものを定義してもループしたりはしないようです。
$ test_cmd () { echo test ; }
$ alias test_cmd=test_cmd
$ test_cmd
test
$ alias test_cmd=test_cmd
$ test_cmd
test
関数に関してはループするので絶対そのまま使ってはいけません。
$ test_cmd () { echo test ; test_cmd; }
$ test_cmd
test
test
test
...
まとめ
(個人的に)よく使うのは、あるコマンドを同じ名前で作ったラッパー関数で便利に使いたい、といった感じのときだと思いますが、
そういった場合には通常はcommand
を付けておけばOKです。
ただし、Zshを使っていてbuiltinコマンドに対してはbuiltin
を使う必要があります。
その昔、
emacs () { command emacs $@ & } }
みたいなのを作ってました1。
他にも人に配布するようなシェルスクリプトで
ユーザー環境によってaliasや関数でコマンドをオーバーライドしてる可能性も考慮すると
なるべく元のコマンドを直接使える様にcommand
とかを使った方が良い場合があります。
通常のシェルスクリプトであればaliasや関数は中で引き継がれませんが、設定ファイルの様な
source
(.
)で実行する様なものは引き継がれるので気をつけないといけません。
\curl
と言った感じでcurl
のaliasを飛ばすものとかよく見ます。
(curlはシェルスクリプトでもよく使うし、aliasでオプションを付けてる人も結構いる?ため。)
関数でラッパーするような行儀悪いことはしないという前提ですが、
これもcommand curl
のが確実です。
-
GUI windowを立ち上げる様にしていて、アプリの様に立ち上げてコマンドラインはそのまま使える、と言った雰囲気にしたかったので。
今はemacsはまず使わないし、使うとしてもno windowモードのが良いのでこの様な設定は害でしかないですが。。。 ↩