これは「まるで」ルールです。

これは「まるで」ルールです。

~からprintfがyashの組み込み部分かどうかに関する質問、来ているこの回答はPOSIX標準を参照しています。

答えは、POSIX検索順序が必要なコマンドの外部実装を見つけ、シェルがそれを組み込みとして実装した場合は組み込みを実行することです。 (サポートされていない組み込み機能の場合特殊内蔵.)

POSIXで内部実装の実行を許可する前に外部実装が必要なのはなぜですか?

…ランダムだと思うので気になりました。

答え1

これは「まるで」ルールです。

つまり、実装が標準の外部コマンドをシェル組み込みとして提供することを決定した場合、ユーザーが見るシェルの動作を変更しないでください。

私が示した比較はhttps://unix.stackexchange.com/a/496291/5132(一方では)PD Korn、MirBSD Korn、およびHeirloom Bourneシェル、(他方では)Z、93 Korn、Bourne Again、およびDebian Almquistシェルの動作の間で(クラッピング手で)この点を強調します。

printf組み込み機能を持たないシェルの場合、呼び出しを削除すると動作が停止し/usr/binます。フィットモードでは、Watanabeシェルが表すPOSIXフィットアクションは同じ結果をもたらします。組み込み関数を持つシェルの動作は次のとおりです。PATHprintfprintfまるで外部コマンドを呼び出しています。

/usr/binただし、すべての資格のないシェルの動作は削除されてもPATH変わりません。いいえ外部コマンドを呼び出すかのように振る舞います。

標準が保証しようとしているのは、シェルがすべての種類の一般的な外部コマンドを組み込むことができ(またはそれを独自のシェル機能として実装することができます)、次のような場合に組み込みで同じ動作を得ることです。検索コマンドを防止するように調整すると、PATH外部コマンドを使用できます。PATH呼び出すことができるコマンドを選択して制御できるツールです。

(説明通りhttps://unix.stackexchange.com/a/448799/5132数年前、人々は再生される内容を変更してUnixの性格を選びましたPATH。 )

命令を出すことだと思うかもしれません。いつも働いて見つけることができるかどうかに関係なくPATH 実はそれがポイントだ一般的な外部コマンドを組み込みます。 (printenv実際、これが私のnoshツールセットにバージョン1.38に組み込みコマンドが含まれている理由です。いいえ地獄のように。 )

しかし、標準では、以下を見ることができることを保証します。同じ汎用外部コマンドの動作は、その関数を呼び出す他の非シェルプログラムで見られるように、シェルでは起動されず、シェルは他のプログラムがPATH同じコマンドを使用して見つからない汎用外部コマンドを魔法のように実行しません。execvpe()PATH。ユーザーの観点から見ると、すべてが一貫して機能し、どのように動作するかPATHを制御するツールです。

追加読書

答え2

これはとてもとんでもないことです。したがって、どのシェルもデフォルトモードでそれを実装しません。

標準理由と説明はい間違った試みだったことを示します。定期的なパスに関連付けられた組み込み関数により、ユーザーはパスの前に独自のバイナリを表示してパスをオーバーライドできます。PATHたとえば、printf関連する組み込み関数は外部コマンドで設定して上書き/usr/bin/printfできます。/foo/bin/printfPATH=/foo/bin:$PATH

しかし、標準では最終的にこれを要求せず、むしろ要求します。その他(また、役に立たず、予想外のことでもあります)。

ここで詳細を読むことができます。エラーレポート。から引用最終承認済みテキスト:

従来の多くの実装では、PATH検索を実行せずに汎用組み込み機能を実行します。この動作は仕様テキストと一致せず、スクリプト作成者が特別に作成されたPATHを介して一般的な組み込みユーティリティをオーバーライドすることはできません。また、その根拠は次のとおりです。著者が取材することが目的である。 PATHを修正して組み込みましたが、これは規範的なテキストが言うものではありません。

FWIW、私はどのシェル実装もテキストの改訂された要件を受け入れなかったと思います。

答え3

その後の比較echoprintf

(次のように、 builtin 「特殊組み込み」、「一般組み込み」はシェルに組み込まれていないため、組み込みとはみなされません。)

最初のPOSIX標準化委員会は、エコーを標準化する方法に同意できなかったため、フラグ(-e,-n,-Eなど)またはエスケープシーケンス(など)を含む引数が\n,\c,\t渡されると、アクションは実行シェルによって決定され、実行シェルによって決定されません。投稿して妥協しました。 POSIXで。代わりに、printfコマンドが追加され、明確に定義された動作が提供されました。 (出典:Classic Shell Scripting、作成者:RobbinsおよびBeebe)

明確に定義されていますが、一部のシェルには組み込みコマンドがありprintfません(例:)。代わりにfromを使用してください。つまり、このシェルで実行されるすべてのスクリプトは、特定のオペレーティングシステム(Ubuntu、Fedoraなど)で同じ内容を印刷しますが、オペレーティングシステム全体で必ずしも同じ内容を印刷するわけではありません(実際には多くのユーザーが理由です)。printfmkshprintf/usr/bin/printf/usr/bin

あるいは、asを組み込んだシェルは、printfオペレーティングシステムに関係なく同じ内容を印刷しますが、シェル実装として使用されている場合にのみ可能です。しかし、printf動作はPOSIX標準によって定義されているので、プログラマはそれを心配する必要はありません。ただし、from を使用するシェルをオーバーライドするPATHと見つかりません。printf/usr/bin/printf

すべてのシェルにはecho組み込み機能がありますが、一部は直接エスケープシーケンスを解釈しますが、ash他のもの(ほとんど)には-eフラグが必要です。動作は POSIX ではなくシェルによって定義されます。

echovs.の主な迷惑の1つprintfは、echo基本的に文字列の末尾に改行文字を印刷しますが、そうでprintfはないということです。新しい行を印刷するにはエスケープシーケンスがprintf 必要です。代わりに、改行文字が印刷されないよう\nにするには、エスケープシーケンス(および可能であればflags)が必要です。echo\c-e

printf動作はPOSIXで定義されているので、最大の移植性のためにお勧めしますが、個人的に各行の末尾に改行文字を明示的に印刷することは非常に迷惑だと思います。 (私が書いたほとんどの行は最後に改行文字を必要とし、抑制する必要はほとんどありません。echo新しい行の印刷)。一方、 echoこれは組み込まれているため常に使用できます($ PATHで見つかる危険はありません)、簡単な確認を実行して-eフラグが必要かどうかを確認し、そのエイリアスを作成できますecho

#! /bin/sh -

# Determine if "builtin" command exists.
BUILTIN='builtin'
  
if ! ("${BUILTIN}" echo 123 >/dev/null 2>&1); then
  BUILTIN=''
fi
export BUILTIN

ECHO='echo -e'

if ${BUILTIN} [ "`echo -e test`" = '-e test' ]; then
  ECHO='echo'
fi
export ECHO

# Now use "${ECHO}" where you would normally use "echo"...

個人的には私はこの方法を好み、printf特別な形式が必要な場合にのみ使用します。

修正する: 私はクレジットが必要な場所に適切なクレジットを提供する必要があります。上記のシェルコードは、shunit2Kate Wardとshunit2開発チームから直接インポートされたものです。 (うまくいきました;))

答え4

また、これを追加してください(クラシックシェルスクリプトロビンスとベビーすごい本):

シェルには多くの組み込みコマンドがあります。これは、別のプロセスで外部プログラムを実行するのではなく、シェル自体がコマンドを実行することを意味します。また、POSIXは「特殊」組み込みコマンドと「一般」組み込みコマンドを区別します。 [ほとんどの一般的な組み込みコマンド]は、シェルが正しく機能するために組み込まれている必要があります(例read:)。効率のために、他のコマンド(およびtrue)がシェルに組み込まれることがよくありますfalse。さらに、この標準は、効率を高めるために追加のコマンドを組み込むことを可能にします。ただし、すべての一般的な組み込み機能は別々のプログラムとしてアクセスでき、他のバイナリプログラムで直接実行できる必要があります。特殊な組み込み機能と一般的な組み込み機能の違いは、シェルが実行するコマンドを検索するときに適用されます。コマンドの検索順序は、最初に特殊な組み込み、次にシェル機能、一般的な組み込み、最後に検索にリストされたディレクトリを検索した外部コマンドです $PATH。この検索順序を使用すると、一般的なシェル組み込み機能を拡張または置換するシェル関数を定義できます。この機能は、対話型シェルで最も一般的に使用されます。たとえば、シェルのプロンプトに現在のディレクトリパス名の最後のコンポーネントが含まれているとします。これを達成する最も簡単な方法は、PS1ディレクトリを変更するたびにシェルを変更することです。cd[この目的のために]独自の[]関数を書くことができます。軟膏の唯一のハエは、ここに小さなハエがあるということです。シェル関数は「実際の」コマンドの機能にどのようにアクセスしますかcd? ...必要なのは、シェルに関数検索をバイパスし、実際のコマンドにアクセスするように指示する「エスケープハッチ」です。これがcommand組み込みコマンドの操作です。

[しかし]このcommandコマンドは特別な組み込みコマンドではありません!コマンドという名前の関数を定義するシェルプログラマは幸運ではありません! POSIX 標準は、特別な組み込みコマンドに対して次の 2 つの追加の特殊機能を提供します。

  1. 特殊な組み込みユーティリティの構文エラーが原因でユーティリティを実行するシェルがクラッシュする可能性がありますが、通常の組み込みユーティリティの構文エラーが原因でユーティリティを実行するシェルは中断しないでください。構文エラーが発生した特別な組み込みユーティリティは、シェルを終了しない場合はゼロ以外の終了値を持つ必要があります。
  2. 特別な組み込みユーティリティを使用して指定された変数の割り当ては、組み込み完了後も維持されます。これは通常の組み込みユーティリティやその他のユーティリティでは発生しません。 [つまり]コマンドの前に変数の割り当てを指定できます。変数は、現在のシェルまたは後続のコマンドの変数に影響を与えず、コマンドが実行される環境でのみその値を持ちます。 (例PATH=/bin:/usr/bin: awk '...':)しかし、これらの割り当てを特別な組み込みコマンドと共に使用する場合、特別な組み込みコマンドが完了した後もその時点から割り当てが有効になります。

アーノルド・ロビンスとネルソンHFビビ。クラシックシェルスクリプト:隠しコマンドはUnixのパフォーマンスを発揮します(262-5ページ)。オライリーメディア。 Kindleのバージョン。

このcommandコマンドを使用すると、シェルは指定されたコマンドと引数を単純なコマンドとして扱うため、シェル関数の検索が抑制されます。 ~からIBM文書

通常、コマンドの前に/(スラッシュ)(特定のパスを表す)がない場合、シェルは次のカテゴリを検索してコマンドを見つけます。

特殊シェル組み込み関数シェル関数一般シェル組み込み関数PATH環境変数たとえば、一般組み込み関数と同じ名前の関数が存在する場合、システムはその関数を使用します。コマンド コマンドを使用すると、関数と同じ名前のコマンドを呼び出し、簡単なコマンドを取得できます。

-vコマンドと-Vコマンドは、シェルが使用するパス名とシェルがコマンドタイプ(組み込み、関数、エイリアスなど)を解釈する方法を標準出力に書き込みます。 -vおよび-Vフラグは現在のシェル環境に関連する出力を生成するため、このコマンドはKornシェルまたはPOSIXシェル汎用組み込みコマンドとして提供されます。 /usr/bin/commandコマンドはサブシェルまたは別のコマンド実行環境で呼び出されるため、正しい結果を生成できない可能性があります。次の例では、シェルはエイリアス、サブルーチン、または特殊なシェルコマンドを認識しません。

(PATH=foo コマンド -v) nohup コマンド -v

builtinそのため、前の例では bash を代わりに使用しました。commandなぜなら、bashをサブシェルに入れても機能しないからです。

私は@mosvyに同意します。標準と仕様テキストが一致しないようです(本当に言わない音です)。

関連情報