コマンドcommand
は最新のPOSIX標準builtin
両方のコマンドが通常の組み込み(つまり、ユーザー定義関数によって上書きされる可能性があります)。一部のシェルでは が定義されていますbuiltin
が、すべてではありません (たとえば、は定義されていません)。一部のシェルで が導入されたdash
理由を理解したいと思います。builtin
私の理解する限りでは、 はbuiltin
特殊な組み込みコマンドのみを返し、次に通常の組み込みコマンドcommand
を返しますが、 は特殊な組み込みコマンド、次に通常の組み込みコマンド、次にパス上のコマンドを返します (また、スイッチを とともに使用して、ユーザーが を変更した場合にシェル定義のデフォルトを使用するように指定-p
できます)。command
$PATH
$PATH
たとえば、 ではmksh
次のようになります。
(注記: mksh
リポジトリからUbuntu 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
また、とbuiltin
は定義済みのエイリアスの検索と使用を「スキップ」することにも注意してください。エイリアスの展開は、POSIX シェルの評価順序では、算術、変数、およびファイルのワイルド カード展開と同様に、コマンドの検索と評価の前に行われます。ただし、算術、変数、およびワイルド カードは と 内で評価されますcommand
が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 posixbuiltins
)、コマンドはcommand
特殊および通常の組み込みも実行します。
例えば
$ 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
逆に、bash ドキュメント
指示
コマンド [-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
振る舞う全く逆他の Bourne スタイルのシェルのコマンドに関してはcommand
...私はzsh
ますます嫌いになってきています...購入者は注意してください (買い手責任負担) たぶん。
更新3:
には組み込みコマンドksh88
がないことにも注意してください。これは で導入されました。 の組み込みコマンドを置き換えるには、エイリアス、関数、引用符の厄介な組み合わせを使用する必要があります。command
ksh93
ksh88
(出典: Robbins, Arnold、Rosenblatt, Bill. 『Learning the Korn Shell: Unix Programming』(p. 456)。O'Reilly Media。Kindle 版。)
これは、@Gilles の「だから、邪悪なことはやめなさい」という回答と一致しています。
答え1
のPOSIXの根拠command
あなたの質問の歴史的な側面のほとんどに答えます。
の指示ユーティリティは第8版のシェルに多少似ている組み込みコマンドですが、指示また、ユーティリティを検索するためにファイルシステムにアクセスすると、名前組み込み直感的ではないでしょう。
(…)指示 -vそして-Vオプションは、現在 3 つの異なる従来のユーティリティによって実現されているユーザーからの要件を満たすために追加されました。タイプSystem Vシェルでは、どこからKornShellでは、どれのC シェルで。
の中に第8版sh組み込みbuiltin
関数は単に関数をバイパスするものとして文書化されていました。
同じ名前で定義された関数に関係なく、組み込みの特殊コマンド (break など) を実行します。
エイリアスはまだ存在していませんでした (エイリアスが登場したときも、それをバイパスするさまざまなメカニズムがありました)。外部コマンドを実行する関数をバイパスしたい場合は、そのフル パスを指定できました。これには、コマンド検索パスにその名前の実行ファイルが複数ある場合に、実行する内容を正確に指定できるという利点がありました。コマンドのフル パスが異なる可能性があるシステム間での移植性は、あまり大きな問題ではありませんでした。builtin
他の方法では実際に実行できない 1 つのことも同様でした。
その後、POSIX が登場し、関数をバイパスする標準的な方法が追加されました。この文脈では、外部コマンドが異なる場所にあるシステムへの移植性が大きな懸念事項であったため、 だけではbuiltin
不十分でした。そのため、command
関数 (およびエイリアス。 はエイリアスが展開されない位置に配置するcommand foo
ため) をバイパスし、標準コマンドを検索する新しい が導入されました。(また、現在 ksh には、まったく異なることを行う という組み込みコマンドがありますが、これが POSIX によって が作成される前か後かはわかりません。) ただし、は意図的に組み込みコマンドをスキップしません。これも移植性の問題によるものです。sh プログラムが標準コマンドを呼び出す場合、このコマンドを組み込みとして提供するかどうかはオペレーティング システムの選択です。 は、特殊な組み込みコマンドの「特殊な組み込み」動作をキャンセルします。これもまた、アプリケーションが組み込みコマンドを呼び出しているかどうかを知る必要がないようにするためです。foo
builtin
command
command
command
POSIXモードではないときにzshが組み込み関数をバイパスする理由がわかりませんcommand
(具体的には、posix_builtins
オプション未設定)。現在の実装はcommand
1996年5月の変更に遡り、zsh 2.6 ベータ 20(「予約語リストから -、exec、noglob、command を削除します」)。その実装では既に POSIX モードの異なる処理が行われていたため、zsh の以前のバージョンとの下位互換性のためだったと推測しますが、それ以上の調査は行っていません。posix_builtins
が設定されていない場合、組み込みコマンドは必ずしも POSIX と互換性があるわけではないため、アプリケーションが特に POSIX コマンドを使用する場合は組み込みコマンドを呼び出さない方がよいため、意図的である可能性がありますcommand
。