rcmdnk's blog
Last update

20180130_aliasfunc_200_200

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のが確実です。

Sponsored Links
  1. GUI windowを立ち上げる様にしていて、アプリの様に立ち上げてコマンドラインはそのまま使える、と言った雰囲気にしたかったので。

    今はemacsはまず使わないし、使うとしてもno windowモードのが良いのでこの様な設定は害でしかないですが。。。

Sponsored Links

« シェルスクリプトでの関数の書き方について sshrc: ssh時に.bashrc設定等をssh先に持っていけるコマンド »

}