printf

printf

echoinはどこにでもあるように見えますが、すべてのシステムが同じ場所(通常)に配置されるわけではありません。それがどこにあるのかわからずに電話する最も安全な方法は何ですか?coreutils/bin/echoecho

echocoreutils バイナリがシステムにない場合、コマンドが失敗しても問題ありません。これは私が望むものとは異なるものをエコーするよりも優れています。

注:ここで動機はechoバイナリを見つけることです。いいえ各シェルの引数セットを見つけるecho 組み込み一貫性があります。例えば、zshにあるか分からないまま内蔵エコーを介してハイフンだけを安全に印刷する方法はないようですbash

答え1

これはcoreutils、GNUシステム用の基本的なUnixユーティリティセットを提供するためにGNUプロジェクトによって開発されたパッケージです。あなたは見つけるでしょうコアツールechoDebianGNUシステム(、、、、、trisquel… )ですぐに動作しますCygwin。他のシステムでは異なる実装を見つけることができます(しばしば動作が異なり、これは最も移植性の低いアプリケーションの1つです)。 FreeBSDにはFreeBSDがあり、ほとんどのLinuxベースのシステムにはbusyboxがあり、AIXにはAIXがあります。FedoraCentOSechoechoechoecho

一部のシステムには複数のシステムがあります(たとえば、およびSolaris /bin/echo/usr/ucb/echo(後者はパッケージの一部であり、現在入手可能なGNUユーティリティパッケージなどの将来のバージョンのSolarisではオプションです/usr/gnu/bin/echo)。これらのうち、CLIは互いに異なります)。

GNUは、coreutilsほとんどのUnixファミリ(MS WindowsなどのUnixファミリではない)システムに移植されているため、ほとんどのマシンでコンパイルできますが、coreutilsおそらくecho望むものではありません。

coreutils echoさらに、バージョン間の非互換性(過去に認識されていない\x41シーケンス)が見つかる可能性があり、その-e動作はPOSIXLY_CORRECT環境(変数)の影響を受ける可能性があります。

echo$PATH他のすべての組み込みプログラムと同様に、(検索で見つかった)ファイルシステムで実行するための一般的なアプローチは次のとおりですenv

env echo this is not the builtin echo

zsh次のこともできます(他のシェルをエミュレートしない場合)。

command echo ...

追加のenvコマンドを実行する必要はありません。

しかし、上記のテキストが移植性に役立たないことを明確にすることを願っています。移植性と安定性のためにprintf代わりに使用してください。

答え2

# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
  or:  /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'

私は正直なところ、これは悪い考えだと思いますが、これはecho合理的な環境でcoreutilsを見つけるためのかなり確実な作業を行います。これはPOSIX互換コマンドです(getconffindshgrepstringsprintfhead)、したがって、どこでも同じように動作する必要があります。getconfデフォルトバージョンが非標準の場合は、まずパス内の各ツールのPOSIX互換バージョンを提供します。

echoこれは、GNUの--help出力と文字通りプログラムテキストに表示される印刷可能な文字列「GNU coreutils」と「標準出力のエコー文字列」を含む実行可能ファイルを探します。複数のレプリカがある場合は、最初に見つかったレプリカをランダムに選択します。見つからないと失敗します。$(...)空の文字列に展開されます。


ただし、システムのどこにもこの(実行可能な)スクリプトがあると問題が発生する可能性があるため、これを「安全」と呼ぶことはありません。

#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /

もう一度申し上げますが、私はこれがとても悪い考えだと思います。既知のハッシュをホワイトリストに追加したくない限り、特定のバージョンを見つけるためのecho合理的で移植可能な方法はありません。安全不明なシステムで実行中です。ある時点では、推測に基づいて何かを実行する必要があります。


私はあなたを励ますprintf代わりにコマンドを使用してください、文字通り使用したい型と引数を受け入れます。

# printf '%s' -e
-e

printfPOSIXでは、フォーマットを提供すると、すべてのシステムが同じように動作するはずです。

答え3

個人的には、echoシェルスクリプトでは完全に使用せず、printf '%s\n' blablabla文字列が短い場合はhere-documentを使用し、文字列が長い場合はhere-documentを使用します。

から引用§11.14 シェル内蔵機能の制限事項~の自動構成マニュアル:

エコ

単純さはecho移植性の問題の最も驚くべき原因かもしれません。echoオプションとエスケープシーケンスの両方を省略しないと、移植可能な使用は不可能です。どのオプションも期待しないでください。

処理に関する合意がないので、引数にバックスラッシュを使用しないでください。の場合echo '\n' | wc -lshソラリス出力2吹くそしてジッシュshシミュレーションモードで)出力1。問題は実際にありますecho。すべてのシェルはこれを'\n'バックスラッシュとn。コマンドを置き換えるとecho 'string\c'内部状態が混乱します。クッシュ 88存在するオペレーティングシステム6.1これにより、s最初の文字と改行文字のみが印刷され、コマンドの置き換えから次のエコーの出力が完全に削除されます。

このため、任意の文字を含む文字列をに渡さないでくださいecho。たとえば、echo "$foo"これを知っている場合にのみ安全です。金持ち値にバックスラッシュを含めることはできず、で始めることはできません-

これが真でない場合は、printf一般的にecho使用する方が安全で簡単ですecho -n。したがって、移植性の大きな問題にならないスクリプトは、printf '%s\n'失敗する可能性がある場合に使用する必要がありechoprintf %s代わりにecho -n移植可能なシェルスクリプトの場合は、次の文書を使用することをお勧めします。

          cat <<EOF
          $foo
          EOF

答え4

正直に言うと、外部バイナリを明示的に呼び出すこと(そして具体的には外部バイナリの特定の実装を見つけること)以外の作業を行うことで、よりうまく解決できない問題はないと確信しています。

だから私は通常、「あなたが望むことをする必要はありません」として帰結する答えを嫌いますが、ここでは例外を置きます。代わりに、私がどれほど強く提案するかに応じて、さまざまな選択肢を提案します。正しいバイナリを見つける必要がある場合は、echoMichael Homerが最高の答えを提供し、Stéphane Chazelasの答えも読んでください。echoこれは、バイナリが見つからないと予想されるファイルシステムの複数の場所が表示されるためです。この回答の最後の部分には、「正しい」エコー検索に関するいくつかの追加注意事項があります。

printf

私はカスタムシェルスクリプトを実際に実行するように設計されたシステムを見たことがなく、過去数十年間に実際に使用されているものを見たことがありませんprintf。 GNUですが、デフォルトでは機能coreutilsしません。printf

私は長期的に見るとシェルスクリプトの移植性と文字通りにアクセスできるという事実に魅了されました。二つprintfBourne Shellのようなものを備えた現在のシステムには、仮想化されたUnix v7(例:約40年前でした)と、基本的に次の機能を備えたAndroidデバイス(私が所有している約5つのうち)はありません。何もないとにかくインストールされてロックされている場合、便利なシェルスクリプトはすぐには実行されません。

これで文字列が印刷されます。正確に、情報 - 約束 - 誰もが現代的に使用する価値があるすべてのシステム:

printf '%s' "$my_var_holding_my_text"

そして

printf '%s' 'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings'

印刷する必要がない限り無効バイト。私はあなたがこれをしなければならないと思います。これにより、テキスト全体を次のようにインポートできなくなります。一つprintfのパラメータそれでもほとんどのシェル(zshここでクレジット)はヌルバイトを文字列終端として使用するためです。したがって、\000書式文字列(最初の引数)に8進エスケープ文字を使用し、%sそれをゼロ個以上の他の引数と組み合わせて他のすべてのテキストを印刷できます。私が知っている限り、16進エスケープ(8進コントラスト)やその他のトリックは移植性が低下します。

提案:捨てないでください何もないあなたはしません必要具体的には、フォーマット文字列に解析/変換されました。さまざまな実装はわずかに異なる形式をサポートします(組み込みや組み込みなどのprintf最新の実装を含む)。printfbashbusybox printf

出力に改行を追加するには、\n書式文字列に改行を追加できます。

printf '%s\n' foo

どこでも厳密に曖昧ではなく同じです。

echo foo

必要な書式文字列を作成するのが簡単ではない複雑な状況にある場合(変数を使用してプログラムで書式文字列を作成することもできます。printf)あります。echo引数なしでデフォルトの文字で区切られます。

ここにファイルを送信するか、次の操作を行います。cat <<DELIMITER

cat <<DELIMITER
$my_variable_containing_my_text
DELIMITER

または

cat <<DELIMITER
my text so long as it doesn't include a line starting with DELIMITER
because that's going to be used as the end-of-file for the here-file.
$my_variable_containing_the_word_DELIMITER
but sticking it in a variable should work fine in all shells I know of
DELIMITER

行が最後から切り離されるかどうかは、ユーザーが制御できません。〜する改行文字で終わります。ほとんどの場合、これはあなたが望むものであっても重要ではないかもしれません。また、多くの(すべて?)シェルはこのファイルを実装するためにディスク上の一時ファイルを使用するため、非常に限られたシステムでこれを許可しない状況に直面する可能性があります(恐ろしく破損したAndroidインスタンスと同じですが、printfSELinuxもありません。ポリシー)あるいは、他の権限制限(正確には覚えていません)のため、シェルは一時ファイルを生成できません。

echoしたがって、コンピュータのセキュリティに関する注意事項から機密情報を印刷する必要がある場合は、正確なシステムに応じて、ここにあるファイルがそれより優れている可能性があります(echo外部であるか組み込みであるか?または他の人が読むことができますか?)と正確な脅威モデル(実行中のプロセス情報よりもディスク上で法医学的検索を実行する可能性が高いですか?)

expr

よく知られていない機能は、expr正規表現マッチングを介してパラメータから部分文字列を抽出して印刷する機能です。これはデフォルトで元の動作echo(新しい行を使用して内容をそのまま印刷)の移植可能なバージョンであり、次のプレーンテキストを印刷するより移植可能な方法ですprintf

expr X"$my_var_holding_my_text" : 'X\(.*\)'

そして

expr X'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings' : 'X\(.*\)'

これはUnix v7に戻ります。X印刷する文字列/変数の前にそして正規表現の前に外部サブパターンが一致/選択する値\( \)は重要です。前者は、印刷中の値がコマンドによってキーワードexprとして誤って解釈されるのを防ぎますが、後者はXが実際に印刷されないことを保証します。expr

awk

以下は、awk受け取るほとんどの単一文字列引数を明示的に印刷する簡単な1行です(最近のバージョンではまだバックスラッシュの問題があります。awkコメントでこれを思い出してくれたStephanに感謝します)。

: | awk 'BEGIN { ORS="" } END { print v }' v="$my_var_with_my_string"

これはUnix v7に戻ります。バックスラッシュがなければ、移植性は非常に優れており、出力する必要があるテキストには十分です。また、awkスクリプトのさまざまな実装の機能テストを書くことが、自分に合ったよりも簡単で/簡単で/クリーンであることがわかります。確かに偏差が多いのですが、echo重要な目標はいくつかの機能を書くのとawk同じくらい良くないからです。 、テストする必要のある変更が少なくなります。echo正確な出力。

変数の代わりにリテラルを使用するには、単一引用符で囲まれた文字列テクニックを使用できます。後に改行文字を追加するには、引数なしechoで操作を実行します(またはコマンドが改行文字を印刷することを確認するために特定の方法を批判的に確認するのに時間をかけてください。左側のno-opコマンドを置き換えることをお勧めしますawk)。 。引数のない:パイプechoですが、Iこのアイデアは完全な移植性のために慎重に検査されませんでした。)。

echosedパイプまたは同様の方法で

入力が特別ではないことを知っていて(文字通り\000印刷したい入力など、バックスラッシュの8進数のエスケープがなく、-文字を具体的に解析する必要がない場合(印刷する場合など)、まだ実行-eできます。できる出力:echoecho

echo X-e | sed '1 s/^X//'

限定的で明確に定義された入力の場合、sedこのような単純な代替方法で問題を解決できます。特定の要件に応じてますます困難になる可能性があります。ある時点では、次の選択肢に進むのが最善です。

機能チェックecho

echoすべての手間を尽くす意思があれば、欲しいものを安定して印刷できないという考えが必ずしも真実ではない。特に所望の出力セットがよく知られている場合にはさらにそうである。私を信じなさい。これはecho、ファイルシステムのどこかで正しいバイナリを検索するよりもはるかに簡単です。

特に安定した文字印刷に関する懸念を表明しました-。残念ながら、まだ完全な機能テストシェルスクリプトフラグメントを作成していませんが、echo以下はいくつかの基本的なフラグメントです。

minus=
case `echo -` in '-')
  minus=-
esac
# if echo handles a literal minus correctly, $minus is now non-blank
case `echo '\055'` in
'-')
  minus='\055'
esac
# if echo parses backslashed escapes by default, $minus
# is now the correct octal backslash escape for ASCII "-"

(またはecho -e '\055'出力する必要があります)、(デフォルトではバックスラッシュエスケープを解析してオフにしたい場合)など、特定の項目に対して同様のテストを設定できます。-e \055-echo -E '\055'

多くの最新のエコーインスタンスは、8進数に加えて他のバックスラッシュエスケープを解析しますが、これら(または他のもの)に対して特別に機能テストを実行できますecho '\x2d'。コンテンツを特に置き換えることなくコンテンツを印刷し、必要な出力をそのまま提供します。

必要に応じてecho -nテストする価値があるかもしれませんが、覚えておいてください。コマンドの置き換え最後の改行文字は常に削除されます(ほとんどのシェルでは最後の改行文字のみ、一部のシェルではすべての末尾の改行文字)。したがって、可能な2つの出力オプションはリテラル-nと空の文字列です。

私の考えでは、これらのツールはエコーを見つけるために最善を尽くし、有効なものが見つからないか他のものが見つからない場合は明示的な印刷を実行するために使用できるため、リソースを参照して取得することもautoconfできます。m4printf

文字通りまた何

正直言って、正しい結果を得るために無差別代入検索をしなければならないことに頼らないことがecho最善だと思います。特定のプログラムがインストールechoされていない、見える場所にインストールされていない、または最初から自動化された無差別代入検索によって、/一部の貧しい人々のシステムがひざまずく可能性があります。

バイナリが指紋としてGNUとして識別される可能性はほとんどありませんが、coreutils echo動作には違いがあります。 GNUが実装をまったく変更しない場合でも、誰かが自分の考えていることをしないように、インストールされているechoGNUバージョンをラップできます。これは愚かな行動です(特別な引数を自動的に削除することを除いて、すべての引数を透過的に渡すと同時に必要に応じて設定することはシェルスクリプトではマイナーな作業であるため、正しいecho --helpテキストを簡単に印刷できますが、echo -e '\055'間違った操作を実行します)。いいえバイナリ徹底した指紋採取は確実です。動作を変更する前に、raw ELFバイナリを編集したことがあり、再編集します。時には非常に便利な機能を有効にするために(プライベートソースメッセージングソフトウェアのUnicodeスマイリーなどの非ASCIIバイトを含むメッセージを自動的に削除することを除く)、シェルのハードコーディングのデフォルトなど、非常にPS1小さい機能のためにに変更されます。の\$\\w \$。個人的にecho私が実際に使用しているシステムでは、ほとんどechoすべての深刻な作業に対してこれを無視するため、これを行うのに十分な理由はありません。しかし、他の人は、基本変数の値を感じるのと同じくらい基本的なecho動作について強く感じることがあります。PS1これで機能テストに戻りましたecho。この時点で上記のセクションを参照してください。

また、私のシステムにGNU coreutilsがechoインストールされているので、gecho有効なPATHインストール場所と名前付きファイルの無差別検索ではechoそのシステムを見つけることができません。

perl実際には、GNUだけがあるシステムよりも望むものを実行する一種のスクリプト言語がインストールされているシステムが多いと確信していますcoreutils echo。一部のスクリプト言語はどこにもあり、ほとんどは1つの実装または明確に定義された仕様を持っていますが、実装は無数であり、echoechoできるだけ多くの他の実装とは少し異なることを実行してください」という1つの仕様に厳密に準拠しています。

関連情報