web-dev-qa-db-ja.com

エコーバイナリを呼び出すための最も安全で移植性の高い方法は何ですか?

echocoreutilsはどこにでもあるようですが、すべてのシステムが同じ場所にあるわけではありません(通常は/bin/echo)。どこにあるかを知らずにこのechoを呼び出す最も安全な方法は何ですか?

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

注:ここでの動機は、echoバイナリを見つけることです。notすべてのシェルのechobuiltinが一貫している引数のセットを見つけることです。 。たとえば、zshbashのどちらにいるかを知らずに、echoビルトインを介してハイフンだけを安全に印刷する方法はないようです。

7
mgiuffrida

coreutilsは、GNUプロジェクトによって開発されたソフトウェアバンドルであり、Unixの基本的なユーティリティのセットをGNUシステムに提供します。coreutils echoGNUシステム(DebiantrisquelCygwinFedoraCentOS...)ですぐに使用できます。システムでは、異なる実装が見つかります(echoは最も移植性の低いアプリケーションの1つであるため、通常は動作が異なります)。FreeBSDにはFreeBSD echoがあり、ほとんどのLinuxベースのシステムにはbusybox echoがあり、AIXにはAIXechoがあります。

一部のシステムには複数のシステムがあります(Solarisでは/bin/echo/usr/ucb/echoなど)(後者はパッケージの一部であり、for GNUすべて異なるCLIを持つ/usr/gnu/bin/echo)を取得するユーティリティパッケージ。

GNU coreutilsはほとんどのUnixライクな(そしてMS Windowsのような非Unixライクな)システムに移植されているので、ほとんどのシステムでcoreutils'echoをコンパイルできますが、それはおそらくあなたが探しているものではありません。

また、coreutilsechoのバージョン間で非互換性があり(たとえば、\x41シーケンスを-eで認識していなかった)、その動作は環境(POSIXLY_CORRECT変数)。

ここで、他のすべてのビルトインと同様に、ファイルシステムからechoを実行するには($PATHのルックアップで見つかります)、一般的な方法はenvを使用することです。

env echo this is not the builtin echo

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

command echo ...

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

しかし、上記のテキストによって、移植性に関して役に立たないことを明確にしたいと思います。 移植性と信頼性のために、代わりにprintfを使用してください

9
# $(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互換のコマンドです( getconffindshgrepstringsprintfhead )なので、どこでも同じです。 getconfは、デフォルトバージョンが非標準である場合に、パスの最初にこれらの各ツールのPOSIX準拠バージョンを提供します。

これは、GNU echo--help出力に表示され、文字通りプログラムテキストにあります。複数のコピーがある場合は、最初に見つかったコピーを任意に選択します。見つからない場合は失敗します。$(...)は空の文字列に展開されます。


ただし、この(実行可能)スクリプトがシステムのどこかに存在すると、いくつかの問題が発生するため、「安全」とは言いません。

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

繰り返しになりますが、これは非常に悪い考えだと思います。既知のechosのハッシュをホワイトリストに登録するつもりがない限り、safe不明なシステムで実行します。ある時点で、推測に基づいて何かを実行する必要があります。


代わりに printfコマンドを使用することをお勧めします 。これは、文字通り使用する形式と引数を受け入れます。

# printf '%s' -e
-e

printfはPOSIXにあり、フォーマットを指定した場合、すべてのシステムで同じように動作するはずです。

7
Michael Homer

個人的には、シェルスクリプトでechoを完全に避け、文字列が短い場合はprintf '%s\n' blablablaを使用し、文字列が長い場合はヒアドキュメントを使用します。

§11.14シェルビルトインの制限 からの引用 autoconfマニュアル

エコー

単純なechoは、おそらく移植性の問題の最も驚くべき原因です。オプションとエスケープシーケンスの両方が省略されていない限り、echoを移植的に使用することはできません。オプションを期待しないでください。

引数にバックスラッシュを使用しないでください。それらの処理にコンセンサスがないためです。 echo '\n' | wc -lの場合、Solarissh2を出力しますが、BashおよびZshshエミュレーションモードの場合)出力1。問題は本当にechoです。すべてのシェルは'\n'をバックスラッシュとnで構成される文字列として理解します。コマンド置換内で、echo 'string\c'AIX 6.1ksh88の内部状態を混乱させて出力します最初の文字sのみ、その後に改行が続き、コマンド置換で次のエコーの出力を完全にドロップします。

これらの問題のため、任意の文字を含む文字列をechoに渡さないでください。たとえば、echo "$foo"は、fooの値に円記号を含めることができず、-で始めることができないことがわかっている場合にのみ安全です。

これが当てはまらない場合、printfは、一般に、echoおよびecho -nよりも安全で使いやすいです。したがって、移植性が大きな問題ではないスクリプトでは、echoが失敗する可能性がある場合は常に、printf '%s\n'を使用し、同様にprintf %sの代わりにecho -nを使用する必要があります。代わりに、ポータブルシェルスクリプトの場合は、次のようなヒアドキュメントを使用することをお勧めします。

          cat <<EOF
          $foo
          EOF
5
Alex Vong