GNU/Linux >> Linux の 問題 >  >> Linux

PrintfがEchoよりも優れているのはなぜですか?

printfと聞きました echoよりも優れています 。 printfを使用しなければならなかった私の経験から、1つのインスタンスしか思い出せません。 echo RHEL 5.8の一部のプログラムに一部のテキストをフィードするためには機能しませんでしたが、printf やりました。しかし、明らかに他にも違いがあるので、それらが何であるか、そしてどちらを使用するかについて特定のケースがあるかどうかを尋ねたいと思います。

承認された回答:

基本的に、これは移植性(および信頼性)の問題です。

最初は、echo オプションを受け入れず、何も拡張しませんでした。スペース文字で区切られ、改行文字で終了する引数を出力するだけでした。

さて、誰かがecho "nt"のようなことができたらいいのにと思いました 改行文字またはタブ文字を出力するか、末尾の改行文字を出力しないオプションがあります。

その後、彼らは一生懸命考えましたが、その機能をシェルに追加する代わりに(perlなど) 二重引用符で囲まれている場合、t 実際にはタブ文字を意味します)、彼らはそれをechoに追加しました 。

David Kornは間違いに気づき、新しい形式のシェル引用符を導入しました:$'...' 後でbashによってコピーされました およびzsh しかし、その時までには遅すぎました。

これで、標準のUNIX echo 2文字のを含む引数を受け取ります およびt 、出力する代わりに、タブ文字を出力します。そして、cが表示されるとすぐに 引数では、出力を停止します(したがって、末尾の改行も出力されません)。

他のシェル/Unixベンダー/バージョンはそれを別の方法で行うことを選択しました:彼らは-eを追加しました エスケープシーケンスを展開するオプション、および-n 末尾の改行を出力しないオプション。一部には-Eがあります エスケープシーケンスを無効にするために、一部には-nがあります ただし、-eではありません 、1つのechoでサポートされているエスケープシーケンスのリスト 実装は必ずしも他の人がサポートしているものと同じではありません。

Sven Mascheckには、問題の範囲を示す素敵なページがあります。

それらのecho オプションをサポートする実装では、通常、--はサポートされません。 オプションの終わりをマークする(echo いくつかの非Bourneに似たシェルが組み込まれており、zshは--をサポートしています。 ただし)、たとえば、"-n"を出力するのは困難です。 echoを使用 多くのシェルで。

bashなどの一部のシェル ¹またはksh93 ²またはyash$ECHO_STYLE 変数)、動作はシェルがどのようにコンパイルされたか、または環境(GNU echo)にも依存します $POSIXLY_CORRECTの場合、の動作も変更されます は環境内にあり、バージョンはzshbsd_echo オプション、一部のpdkshベースのposix オプション、またはそれらがshとして呼び出されるかどうか か否か)。つまり、2つのbash echo s、同じバージョンのbashからでも 同じように動作することは保証されていません。

POSIXによると:最初の引数が-nの場合 または、引数に円記号が含まれている場合、動作は指定されていませんbash その点でのechoは、たとえばecho -eという点ではPOSIXではありません。 -e<newline>を出力していません POSIXが要求するように。 UNIX仕様はより厳密であり、-nを禁止しています。 cを含むいくつかのエスケープシーケンスの拡張が必要です 1つは出力を停止します。

多くの実装が準拠していないことを考えると、これらの仕様はここでは実際には役に立ちません。一部の認定でも macOSのようなシステムは準拠していません。

現在の現実を実際に表すために、POSIXは実際に次のように言う必要があります。最初の引数が^-([eEn]*|-help|-version)$と一致する場合 拡張正規表現または任意の引数にバックスラッシュ(またはαのようなバックスラッシュ文字のエンコーディングがエンコーディングに含まれている文字)が含まれている BIG5文字セットを使用するロケールでは、動作は指定されていません。

全体として、echo "$var"が何であるかわかりません $varを確認できない限り、出力されます バックスラッシュ文字を含まず、--で始まらない 。 POSIX仕様では、実際にはprintfを使用するように指示されています。 その場合は代わりに。

つまり、echoは使用できないということです。 制御されていないデータを表示します。つまり、スクリプトを作成していて、外部入力(ユーザーからの引数、またはファイルシステムからのファイル名など)を受け取っている場合、echoを使用することはできません。 表示します。

これで問題ありません:

echo >&2 Invalid file.

これはそうではありません:

echo >&2 "Invalid file: $file"

(一部の(UNIX非準拠)echoでは問題なく動作しますが bashのような実装 xpg_echoのとき オプションは、コンパイル時や環境を介した場合など、何らかの方法で有効にされていません。

関連:パイプラインで`ping`からの出力の各行をすぐに処理しますか?

file=$(echo "$var" | tr ' ' _) ほとんどの実装ではOKではありません(yashを除く) ECHO_STYLE=rawを使用 (yashという警告付き の変数は任意のバイトシーケンスを保持できないため、任意のファイル名を保持できません)およびzshecho -E - "$var"

printf 一方、少なくともechoの基本的な使用法に限定されている場合は、より信頼性が高くなります。 。

printf '%sn' "$var"

$varのコンテンツを出力します 含まれている可能性のある文字に関係なく、改行文字が続きます。

printf '%s' "$var"

末尾の改行文字なしで出力します。

現在、printfにも違いがあります 実装。 POSIXで指定されている機能のコアがありますが、多くの拡張機能があります。たとえば、%qをサポートするものもあります 引数を引用しますが、その方法はシェルごとに異なり、uxxxxをサポートするものもあります。 Unicode文字の場合。 printf '%10sn' "$var"では動作が異なります マルチバイトロケールでは、printf %b '123'には少なくとも3つの異なる結果があります

しかし、結局のところ、printfのPOSIX機能セットに固執する場合 あまり凝ったことをしようとしないでください。問題はありません。

ただし、最初の引数は形式であるため、可変/制御されていないデータを含めないでください。

より信頼性の高いecho printfを使用して実装できます 、のように:

echo() ( # subshell for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%sn' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%bn' "$*"
)

サブシェル(ほとんどのシェル実装で追加のプロセスを生成することを意味します)は、local IFSを使用して回避できます。 多くのシェルを使用するか、次のように記述します:

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s "$1"
     shift
     if [ "$#" -gt 0 ]; then
       printf ' %s' "[email protected]"
     fi
  fi
  printf 'n'
}

メモ

1。どのようにbashecho 動作は変更できます。

bashを使用 、実行時に、echoの動作を制御するものが2つあります。 (enable -n echoの横にあります またはechoを再定義する 関数またはエイリアスとして):
xpg_echo bash オプションとbashかどうか posixモードです。 posix bashの場合、モードを有効にできます shと呼ばれます またはPOSIXLY_CORRECT 環境内またはposixを使用している オプション:

ほとんどのシステムのデフォルトの動作:

  $ bash -c'echo -n "
Linux
  1. LinuxVPSがWindowsVPSよりも優れているのはなぜですか

  2. Linux と Windows – Linux がプログラミングと Web 開発に適している理由

  3. シェルで 10 個を超えるパラメーターを処理する方法

  1. mmap() がシーケンシャル IO よりも速いのはなぜですか?

  2. エコー改行文字が bash で機能しない

  3. (Ubuntu) Linux のファイル コピー アルゴリズムは Windows 7 より優れていますか?

  1. [Linux]:MTRを使用してネットワークの問題を診断– tracerouteよりも優れています!

  2. チルダ(〜)が二重引用符の内側に展開されないのはなぜですか?

  3. 「sudo」パスワードが「su root」パスワードと異なるのはなぜですか