
のecho
はcoreutils
どこにでもあるようですが、すべてのシステムで同じ場所 (通常は ) にあるわけではありません。どこにあるか分からない場合、/bin/echo
これを呼び出す最も安全な方法は何ですか?echo
coreutilsecho
バイナリがシステムに存在しない場合にコマンドが失敗しても問題ありません。これは、希望するものと異なるものを echo するよりはましです。
注: ここでの目的はecho
バイナリを見つけることです。ないすべてのシェルの引数セットを見つけるにはecho
組み込みは一貫しています。たとえば、zsh
または のどちらにいるかがわからずに、 echo 組み込み関数を使用してハイフンだけを安全に印刷する方法はないようです。bash
答え1
はcoreutils
GNUプロジェクトによって開発されたソフトウェアバンドルであり、GNUシステムにUnixの基本ユーティリティのセットを提供します。コアユーティリティecho
Debian
GNU システム ( 、、、、... )ではすぐに使用できます。他のシステムでは、異なる (通常は動作が異なり、移植性が最も低いアプリケーションの 1 つである) 実装が見つかります。FreeBSD には FreeBSDtrisquel
が、ほとんどの Linux ベースのシステムには busybox が、AIX には AIX が付属します...Cygwin
Fedora
CentOS
echo
echo
echo
echo
システムによっては、複数の が存在する場合もあります (Solaris の や/bin/echo
など/usr/ucb/echo
(後者は、 を取得する GNU ユーティリティ パッケージの など、Solaris の新しいバージョンではオプションになった パッケージの一部です/usr/gnu/bin/echo
)。これらはすべて異なる CLI を備えています)。
GNU はほとんどの Unix 系 (さらには MS Windows などの非 Unix 系) システムに移植されているため、ほとんどのシステムで'coreutils
をコンパイルできますが、おそらくそれはあなたが探しているものではありません。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'
正直言ってこれは悪いアイデアだと思うが、これは適切な環境で coreutils を見つけるのにかなりしっかりした仕事をするだろうecho
。これらは最初から最後まで POSIX 互換のコマンドである (getconf
、find
、sh
、grep
、strings
、printf
、head
) なので、どこでも同じように動作するはずです。 は、getconf
デフォルトのバージョンが非標準の場合に、パスの最初に各ツールの POSIX 準拠バージョンを提供します。
echo
GNU の出力に表示され、プログラム テキスト内に文字通り存在する、印刷可能な文字列「GNU coreutils」と「Echo the STRING(s) to standard output」の両方を含む実行可能ファイルを検索します--help
。複数のコピーがある場合は、最初に見つかったコピーが任意に選択されます。何も見つからない場合は失敗し、$(...)
空の文字列に展開されます。
ただし、この (実行可能な) スクリプトがシステム上のどこかに存在すると、何らかの問題が発生する可能性があるため、これを「安全」と呼ぶことはできません。
#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /
繰り返しになりますが、これは非常に悪いアイデアだと思います。既知のハッシュをホワイトリストに登録しない限りecho
、特定のバージョンを見つける合理的でポータブルな方法はありません。安全未知のシステムで実行します。ある時点で、推測に基づいて何かを実行する必要があります。
私はあなたにprintf
代わりにコマンドを使用してくださいは、フォーマットと、文字通りに使用したい引数を受け入れます。
# printf '%s' -e
-e
printf
POSIX であり、フォーマットを指定すればすべてのシステムで同じように動作するはずです。
答え3
echo
個人的には、シェル スクリプトでは完全に避け、printf '%s\n' blablabla
文字列が短い場合は を使用し、文字列が長い場合は here-document を使用します。
引用元§11.14 シェル組み込み関数の制限のautoconf マニュアル:
エコー
単純なこと
echo
は、おそらく移植性の問題の最も意外な原因です。オプションとエスケープ シーケンスの両方を省略しない限り、移植性を維持することはできませんecho
。オプションは期待しないでください。引数にバックスラッシュを使用しないでください。その扱いについては合意が得られていませ
echo '\n' | wc -l
んsh
。ソラリス出力します2
が、バッシュそしてズッシュ(sh
エミュレーションモードで)出力1
。問題はecho
、すべてのシェルが'\n'
バックスラッシュと.で構成される文字列として理解することですn
。コマンド置換内では、echo 'string\c'
の内部状態が混乱します。キシュ88の上6.1 の場合最初の文字のみを出力しs
、その後に改行を出力し、コマンド置換で次の echo の出力を完全に削除します。これらの問題のため、任意の文字を含む文字列を に渡さないでください
echo
。たとえば、echo "$foo"
は、次のことがわかっている場合にのみ安全です。フーの値にはバックスラッシュを含めることはできず、 で始まることもできません-
。これが当てはまらない場合は、一般的にや
printf
よりも の方が安全で使いやすいです。したがって、移植性がそれほど重要でないスクリプトでは、失敗する可能性がある場合は を使用し、同様に の代わりに を使用する必要があります。移植可能なシェル スクリプトの場合は、代わりに次のようなヒアドキュメントを使用することをお勧めします。echo
echo -n
printf '%s\n'
echo
printf %s
echo -n
cat <<EOF
$foo
EOF
答え4
正直なところ、外部バイナリを明示的に呼び出す以外の方法 (特に外部バイナリの特定の実装を探す) で解決できない問題はないと思います。
なので、私は通常、「あなたがしたいことをする必要はないはずです」という結論に落ち着く回答を嫌うのですが、ここでは例外を設けています。代わりに、私が強く推奨する順に、さまざまな代替案を提案します。正しいecho
バイナリを絶対に見つけなければならない場合は、Michael Homer の回答が最も適切です。また、Stéphane Chazelas の回答も読む必要があります。この回答では、バイナリが見つかるとは思わないファイルシステム内の場所がいくつか示されているからですecho
。また、この回答の最後のセクションでは、「正しい」エコーの検索に関する追加の注意事項もいくつか示しています。
printf
実際にカスタム シェル スクリプトを実行することを目的としており、過去 20 ~ 30 年間で実際に使用されたシステムで、 が付属していないものを私は見たことがありません。また、GNU のような大規模な機能を搭載することに近い機能を備えたシステムで、 が付属していないものをprintf
私は見たことがありません。coreutils
printf
視点を変えれば、私はシェルスクリプトの移植性に異常なほど執着しており、文字通り二現在、Bourneのようなシェルを持つシステムには、printf
仮想化されたUnix v7(そう、40年ほど前のものです)、およびAndroidデバイス(所有している5台のうちの1台)があり、基本的に何もないインストールされていて、非常にロックダウンされているため、いずれにしてもすぐには役に立つシェル スクリプトは実行されません。
これにより文字列が印刷されますその通り、私は約束します、現代の誰もが使用する価値のあるあらゆるシステムについて:
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'
印刷する必要がない限りヌルバイト。そうする必要はないと思います。そうすると、テキスト全体を1つprintf への引数ともかくほとんどのシェル (zsh
ここでは賞賛に値します) は文字列の終端文字としてヌル バイトを使用するためです。したがって、\000
フォーマット文字列 (最初の引数) 内で 8 進エスケープを使用し、それを 0 個以上の%s
引数と 0 個以上のその他の引数と組み合わせて、他のすべてのテキストを出力します。私の知る限り、16 進エスケープ (8 進エスケープと比較) やその他のトリックは移植性が低くなります。
提案: 入れないでください何でもあなたはしない必要特別に解析/フォーマット文字列に変換されます。printf
実装によって、サポートされるフォーマットが若干異なります (最新のprintf
実装を含む、例:bash
組み込みとbusybox printf
)。
出力に改行を追加したい場合は、\n
フォーマット文字列に次のコードを追加するだけで済みます。
printf '%s\n' foo
は、厳密に明確で、どこでも同じように機能する同等のものである。
echo foo
必要な書式文字列の構築が容易ではない複雑な状況に陥った場合 (書式文字列は変数を使用してプログラム的に構築することもできることに注意してください)、 に渡す引数に常に改行リテラルを含めることprintf
も、引数なしの bare を使用して改行文字のみを別途出力することもできecho
ます。
Here-files、または: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
唯一の注意点は、最後に改行を入れるかどうかは制御できないことです。常に意思最後に改行が入ります。ほとんどの場合、これがおそらくあなたが望んでいたことであり、あるいは問題ではありません。また、多くの (すべての?) シェルは、ヒアファイルを実装するためにディスク上の一時ファイルを使用するため、非常に制限されたシステムでこれが許可されない状況に遭遇する可能性があります (printf
私が持っている、ひどく機能不全の Android インスタンスには、シェルが一時ファイルを作成するのを妨げる SELinux ポリシーまたはその他の権限制限 (正確には覚えていません) もあります)。
このため、コンピュータ セキュリティの観点から、機密情報を印刷する必要がある場合、echo
正確なシステム (echo
外部か組み込みか? /proc/$PID は誰でも読み取り可能かユーザーが読み取り可能か? here-file はユーザーまたは世界で読み取り可能か?) と正確な脅威モデル (脅威は実行中のプロセス情報よりもディスクをフォレンジック検索する可能性が高いか?) に応じて、here-file の方が よりも悪い場合も、良い場合もあります。
expr
のあまり知られていない機能は、expr
正規表現に一致する引数内から部分文字列を抽出して印刷できることです。これは基本的に、元のecho
動作 (内容をそのまま印刷し、改行文字を 1 つ追加する) の移植性の高いバージョンであり、 よりもさらに移植性の高いプレーン テキストの印刷方法です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
として誤って解釈されるのを防ぎexpr
、後者は、X が実際には印刷されないようにします。
awk
以下は、awk
受け取ったほとんどの単一文字列引数を一意に印刷するコンパクトなワンライナーです (最新バージョンではバックスラッシュの問題がまだあります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
echo
パイプ経由sed
または類似の
入力が特殊でないことが分かっている場合(\000
文字通り印刷したい入力のようにバックスラッシュ付きの 8 進エスケープがなく、-
文字を特別に解析することを避ける必要がある場合、たとえば を印刷したい場合) 、の出力を前処理するために使用できる他のものがあれば、 を引き続き-e
任意の作業に使用できます。echo
echo
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'
(デフォルトでバックスラッシュ エスケープを解析していて、それをオフにしてみたい場合) など。
echo の最近のインスタンスの多くは、8 進数以外のバックスラッシュ エスケープも解析しますが、それらの機能テストを具体的に行うこともできます (echo '\x2d'
または何でも)。ただし、ほとんどの場合、echo に渡して、内容に特別な置換を行わずに印刷できる引数のセットを見つけて、必要な出力をそのまま渡すだけでよいと思います。
ニーズに応じて、echo -n
テストする価値があるかもしれませんが、コマンド置換常に最後の改行を削除します (ほとんどのシェルでは最後の改行だけを削除しますが、一部のシェルでは末尾の改行すべてを削除します)。そのため、出力オプションとして考えられるのは、リテラル-n
と空の文字列の 2 つです。
autoconf
また、やソースを参照することもできます。m4
これらのツールは、 や他の有効な手段が見つからない場合に、明確な印刷を行うために使用できる echo を探すために特別な努力をするからですprintf
。
文字通り他のもの
まさに正しいものを総当たりで探す必要がないものなら何でも良いと、私は心から思っています。特定のものがインストールされないか、探している場所にインストールされないか、または自動総当たり検索によって気の毒な人のシステムが極端に遅くなるecho
可能性が非常に高いです。echo
/
そして、非常に可能性が低いですが、バイナリが GNU であるという指紋をパスするcoreutils
echo
かもしれませんが、動作が異なる可能性があります。GNU が実装を変更しなくても、誰かがインストールした GNU のバージョンをラップして、echo
愚かな動作と思われる動作をしないようにする可能性があります (特別な引数を黙って削除し、必要な引数を設定すること以外は、すべての引数を透過的に渡すことはシェルスクリプトでは簡単なので、echo --help
正しいテキストを印刷してもecho -e '\055'
間違った動作をすることは簡単にできます)。そして、バイナリ徹底的なフィンガープリントを通過することは確かです。私は以前にも動作を変更するために生の ELF バイナリを編集したことがあり、またそうするつもりです。非常に便利な機能を有効にするため (クローズドソースのメッセージング ソフトウェアで Unicode スマイリーなどの非 ASCII バイトを含むメッセージを黙って削除しないようにするため) もあれば、PS1
シェルのハードコードされたデフォルトを\$\
ではなく に変更するなど、本当に些細なことの場合もあります。個人的には、実際に使用しているシステムでは、ほとんどすべての本格的な作業で を無視しているため、\w \$
を にする十分な理由はありませんが、デフォルトの動作について、デフォルトの変数値について私と同じくらい強く感じている人がいるかもしれません。そのため、機能テスト に戻り、その時点で上記のセクションを参照してください。echo
echo
echo
PS1
echo
また、私のシステムでは GNU coreutils がecho
としてインストールされているgecho
ため、 およびおそらくインストール場所の効率的な検索PATH
や という名前のファイルのみのブルートフォース検索ではecho
、これらのシステムは検出されないことに注意してください。
そして、perl
GNU 専用にインストールされているシステムよりも、必要な機能を実行できるようなスクリプト言語がインストールされているシステムのcoreutils
echo
方が多いと私は確信しています。一部のスクリプト言語は普及しており、ほとんどの場合、1 つの実装または明確に定義された仕様を持っていますが、echo
実装は無数にあり、「可能な限り他の実装とは少し異なることを行う」という 1 つの仕様に正確に従いますecho
。