Makefileでecho -eが奇妙に動作するのはなぜですか?

Makefileでecho -eが奇妙に動作するのはなぜですか?

私はMakefile(該当する場合はUbuntu 20.04で)を作成していますecho

test.txt:
        @echo -e 'hello\nworld'
        @echo -e 'hello\nworld' > test.txt

を実行すると、makestdoutでもtest.txtと同じ内容が表示されると予想されますが、実際にはそうではありません。標準出力からこれを取得します。

hello
world

しかし、これはtest.txtにあります。

-e hello
world

一方-e、Makefileから次の2行を削除すると、標準出力が表示されます。

hello\nworld

test.txtから:

hello
world

これにより、リダイレクトが検出され、他の動作が見えるかどうか疑問に思いますecho。ただし、シェルで手動で実行する場合はそうではありません/bin/echo -e 'hello\nworld' > test.txt(通常、予想どおり別々の行に合計が生成されますhello)。world私は行を追加して、Makefileが組み込みシェルではなく/bin/echoを使用していることを確認しました@echo --version

ここで何が起こっているのでしょうか?

答え1

echoそこから出力するにはUNIX互換の実装が必要です-e<space>hello<newline>world<newline>

要件を満たしていない方は資格がありません。多くの場合、そうではありません。つまり、ポータブルを使用することはほとんど不可能ですechoprintf使用すべき。の一部(ほとんど)バージョンでは、オプションがすべて有効になっている場合にのみ互換性bashがあります。これはおそらく予想される動作です。echoposixxpg_echoecho

echoGNUに付属のスタンドアロンユーティリティも同様です。これは、coreutils環境でsetを使用して呼び出す場合$POSIXLY_CORRECTにのみ互換性があり、まったく新しいバージョンです。

make通常、sh各タスクラインのコマンドラインを解釈するために実行します。

makeしかし、最適化として、GNU実装は、コードが十分に単純であり、それを解釈するためにシェルを呼び出す必要がないと思う場合は、コマンドを直接実行できます。

これはecho --version与えられた理由を説明し/bin/echoますが、echo ... > fileリダイレクトを実行するにはシェルが必要です。

strace -fe execve make以下を使用して実行された内容を確認できます(makeLinuxでない場合はシステムでtruss/ ...そのエントリを使用)。tusc

ここでは/bin/echo互換性がありませんが、sh組み込まれてechoいます。

printfecho拡張スタイルのエスケープシーケンスが必要な場合は、ここで使用してください。

printf '%b\n' 'hello\nworld'

その中に滞在パラメータ、Cスタイルエスケープシーケンスの理解(printf(echo)および(C)8進数シーケンスのエコスタイルエスケープシーケンスと区別されます)\0xxx\xxx

printf 'hello\nworld\n'

ここでも次のことができます。

printf '%s\n' hello world

これは、複数のパラメータを別々の行に出力する一般的で移植可能な方法です。

もう一つの方法は、以下を追加することです。

SHELL = bash

コマンドラインを解釈する代わりに、Makefileformake呼び出しbash(インストールされていると仮定)を使用します。またはに電話してください。$PATHshmakemake <target> SHELL=bash

bashますます多くのシステムがデフォルトでインストールされているため、これは必ずしも移植性が高くなるわけではありませんが、一部のシステム(Solarisなど)は基本的に標準的な方法で実行されるようにbash構築されています。echo

SHELL他に設定すると、/bin/sh上記のGNU最適化を無効にする副作用があるため、またはを使用すると、bashへの依存関係を追加せずに呼び出し間で一貫した動作を得ることができます。makemake <target> SHELL=shmake <target> SHELL=/bin//sh

関連情報