シェルスクリプトの `command` コマンドと `buildin` コマンドの違い

シェルスクリプトの `command` コマンドと `buildin` コマンドの違い

私は命令がcommand次にあることを知っています。最新のPOSIX規格しかしbuiltinそれは真実ではない。私も両方のコマンドが一般的な組み込み機能(つまり、ユーザー定義関数でオーバーライドできます)、一部のシェルではこれを定義しますが、すべてではありませんbuiltin(例:dashなし)。なぜbuiltinいくつかのシェルに導入されたのか理解したいと思います。

私が知っている限り、特殊な組み込みbuiltin機能のみが返され、その後は一般的な組み込み機能が返されますが、特殊な組み込み機能が返され、次に一般的な組み込み機能が返され、次にパスのcommandコマンドが返されます(そしてスイッチを使用して指定-pできます)。commandこれはシェルで定義されたデフォルト値を使用します($PATHユーザーの変更を防ぐため$PATH)。

たとえば、次のようにmkshなります。

メモ: mkshUbuntu 20.04のリポジトリにインストールされているhttp://archive.ubuntu.com/ubuntu focal/universe amd64 mksh amd64 58-1

$ echo $KSH_VERSION
@(#)MIRBSD KSH R58 2020/03/27
$ which -a echo
/usr/bin/echo
/bin/echo
$ which -a printf
/usr/bin/printf
/bin/printf
$ type echo
echo is a shell builtin
$ type printf
printf is /usr/bin/printf
$ command echo 'Hello World!'
Hello World!
$ command printf 'Hello World!\n'
Hello World!
$ builtin echo 'Hello World!'
Hello World!
$ builtin printf 'Hello World!\n'
mksh: builtin: printf: not found
$ sudo cp /usr/bin/printf /usr/bin/printf.backup
$ sudo cp /bin/printf /bin/printf.backup
$ sudo rm /usr/bin/printf
$ sudo rm /bin/printf
rm: cannot remove '/bin/printf': No such file or directory
$ sudo cp /usr/bin/printf.backup ~/printf
$ echo $PATH | sed 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ export PATH=~:$PATH
$ echo $PATH | sed 's/:/\n/g'
/home/my_username
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ command printf 'Hello World!\n'
Hello World!
$ command -p printf 'Hello World!\n'
mksh: printf: inaccessible or not found

私の理解は正しいですか?それともまったく同じことをcommandしますかbuiltin(もしそうなら、なぜbuiltin導入されたのですか?)?それともcommand間に別の微妙な違いがありますかbuiltin

(StackExchangeで回答を探してみましたが、何も見つかりませんでしたが、もし適切な回答を教えてください。

修正する:

検索を「スキップして」定義されたエイリアスを使用するcommandことも注目に値します。builtinPOSIXシェル評価順序では、エイリアス拡張は、算術、変数、およびファイルワイルドカード拡張と同様に、コマンドの検索と評価の前に表示されます。ただし、算術、変数、およびワイルドカードcommandは sums 内で評価されますが、builtinエイリアスでは評価されません。ドキュメントに何か言及する必要があるようです。

たとえば、

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ command echo $((1 + 1))
2
$ builtin echo $((3 + 1))
4

しかし、

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ alias \[='echo hello'
$ [
hello
$ if builtin [ 'north' != 'south' ]; then echo 'what a world!'; fi
what a world!

アップデート2:

私はいくつかの調査と実験の後、zshその行動完全に反対POSIX 標準と他の Bourne スタイルのシェルとコマンドcommandの関係。

~からzsh ドキュメント(セクション6.2、プレフィックスコマンド修飾子)

コマンド[-pvV]
命令は、シェル関数や組み込みコマンドの名前ではなく、外部コマンドの名前として扱われます。

例えば

$ echo ${ZSH_VERSION}
5.8
$ command cd ~
zsh: command not found: cd
$ command -p cd ~
zsh: command not found: cd
$ command echo 'hi'
hi
# `echo` is a regular builtin just like `cd` though...??!!!
$ set -o |grep posix
posixaliases          off
posixargzero          off
posixbuiltins         off
posixcd               off
posixidentifiers      off
posixjobs             off
posixstrings          off
posixtraps            off
$ cd() { echo 'I told you.'; }
$ cd
I told you.
# ????!!!!

このコマンドは、POSIX_BUILTINS環境変数が設定されている場合にのみ(を使用して)特殊および一般的な組み込みコマンドを実行します。set -o posixbuiltinscommand

例えば

$ echo ${ZSH_VERSION}
5.8
$ cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var
$ set -o posixbuiltins
$ command cd ~
$ ls
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
$ command -p cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var

一方、バッシュ文書

注文する
コマンド[-pVv]コマンド[パラメータ...]

commandという名前のシェル関数を無視し、引数を使用してコマンドを実行します。シェル組み込みコマンドまたはPATHを検索して、見つかったコマンドのみを実行します。

例えば

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var
$ set +o posix  # Turn off POSIX mode
$ command cd ~
$ ls
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
$ command -p cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var

だからzsh行動完全に反対command他のBourneスタイルのコマンドシェルについては...zshますます嫌がり始めました...買い手は注意してください(警告)私の考えでは。

アップデート3:

組み込みコマンドがksh88ないことも注目に値します。commandこれはで紹介されましたksh93。に組み込まれている関数を置き換えるには、ksh88エイリアス、関数、および参照を混乱させて使用する必要があります。

出典:Robbins、Arnold; Rosenblatt, Bill. Kornシェル学習:Unixプログラミング(456ページ)。オライリーメディア。 Kindleのバージョン。)

これは@Gillesの「だから邪悪な行動を止める」という答えと一致しています。

答え1

これPOSIXの正当化commandあなたの質問のほとんどの歴史的側面に答えます。

これ注文する8版シェルにやや似たユーティリティ組み込み命令を受けたが、それ以来注文するまた、ファイルシステム検索ユーティリティに移動して名前を指定します。組み込み直感的ではありません。

(…)これ注文する -Vそして-Vこのオプションは、現在3つの異なる履歴ユーティリティを介して実行されるユーザーの要件を満たすために追加されました。タイプSystem V シェルではどこKornShellからどのCシェルから。

内部に8枚目builtin組み込み機能はバイパス機能として文書化されています。

同じ名前の定義された関数に関係なく、組み込みの特殊コマンド(例:break)を実行します。

エイリアスはまだ存在しません(もし存在すれば、エイリアスをバイパスする他のメカニズムがあります)。外部コマンドを実行するために関数をバイパスしたい場合は、フルパスを指定できます。これには、コマンド検索実行可能ファイルにその名前の関数が複数ある場合は、実行したい項目を正確に指定できるという利点があります。コマンドのフルパスが異なる可能性があるシステム間の移植性は一般的な問題ではありません。builtin実際に他の方法ではできないことも同様です。

後でPOSIXが登場し、機能をバイパスする標準的な方法を追加しました。この場合、外部コマンドを別の場所にあるシステムに移植することは大きな問題であるため、builtin十分ではありません。したがって、新しいコマンドはcommand機能(エイリアスが拡張されない場所にcommand foo配置されたエイリアスを含む)をバイパスし、標準コマンドを見つけます。foo。 (現在、kshにはまったく異なる操作を実行する組み込み機能がありますが、builtinPOSIXが作成される前か後かはわかりませんcommand。)しかし、command組み込み機能は移植性の理由で意図的にスキップされません。 if shプログラムが標準コマンドを呼び出すと、オペレーティングシステムはこのコマンドを組み込みコマンドとして提供するかどうかを選択できます。commandアプリケーションが組み込み関数を呼び出すかどうかを知る必要がないように、特別な組み込みに対する「特殊組み込み」の動作を再び抑制します。

commandPOSIXモードではない場合、zshが組み込み機能をバイパスする理由がわかりません(特に次の場合)。posix_builtinsオプション未設定)。現在の実装は、command1996年5月に発表された変更点にさかのぼります。zsh 2.6ベータ20"予約語リストから -、exec、noglob、およびコマンドを削除します。")。実装はすでにPOSIXモードを異なる方法で処理しているため、以前のバージョンのzshとの互換性のためであると仮定していましたが、これ以上調査しませんでした。posix_builtins組み込みコマンドが設定されていない場合、必ずしもPOSIXと互換性があるわけではないので、これは意図的なものかもしれません。したがって、アプリケーションが特定のPOSIXコマンドを使用している場合は、そのコマンドを呼び出さないことをお勧めしますcommand

関連情報