makefileの複数行bashコマンド


120

数行のbashコマンドを使用してプロジェクトをコンパイルする非常に快適な方法があります。しかし、今私はそれをmakefileでコンパイルする必要があります。すべてのコマンドが独自のシェルで実行されることを考えると、私の質問は、メイクファイルで互いに依存している複数行のbashコマンドを実行する最良の方法何ですか? たとえば、次のように:

for i in `find`
do
    all="$all $i"
done
gcc $all

また、誰かが単一行のコマンドでさえbash -c 'a=3; echo $a > file'ターミナルで正しく機能する理由を説明できますが、メイクファイルの場合は空のファイルを作成しますか?


4
2番目の部分は別の質問でなければなりません。別の質問を追加すると、ノイズが発生します。
l0b0

回答:


136

行の継続にはバックスラッシュを使用できます。ただし、シェルはコマンド全体を1行に連結して受け取るため、セミコロンで行の一部を終了する必要があることに注意してください。

foo:
    for i in `find`;     \
    do                   \
        all="$$all $$i"; \
    done;                \
    gcc $$all

しかし、find呼び出しによって返されたリスト全体を取得してに渡したいだけの場合gcc、実際には必ずしも複数行のコマンドは必要ありません。

foo:
    gcc `find`

または、より一般$(command)的なシェルのアプローチを使用します($ただし、エスケープすることに注意してください)。

foo:
    gcc $$(find)

2
複数行の場合、他の場所のコメントに示されているように、ドル記号をエスケープする必要があります。
tripleee

1
はい、短い回答1. $:= $$ 2. \でマルチラインを追加BTW bashの連中は通常、 `find`呼び出しを$$(find)で置き換えることをお勧めします
Yauhen Yakimovich

89

質問に示されているように、すべてのサブコマンドは独自のシェルで実行されます。これにより、重要なシェルスクリプトを書くの少し面倒になります- しかし、それは可能です! 解決策は、スクリプトをmakeが単一のサブコマンド(単一行)と見なすものに統合することです。

makefile内でシェルスクリプトを作成するためのヒント:

  1. $置き換えて、スクリプトの使用を回避します$$
  2. ;コマンドの間に挿入して、スクリプトを1行として機能するように変換します
  3. スクリプトを複数行で記述したい場合は、行末をエスケープしてください \
  4. オプションでset -e、サブコマンドの失敗時に中止するmakeのプロビジョンと一致するように開始します
  5. これは完全にオプションですが、複数行のシーケンスのまとまりを強調()または{}強調するためにスクリプトを括弧で囲むことができます-これは典型的なmakefileコマンドシーケンスではありません

OPから着想を得た例を次に示します。

mytarget:
    { \
    set -e ;\
    msg="header:" ;\
    for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
    msg="$$msg :footer" ;\
    echo msg=$$msg ;\
    }

4
SHELL := /bin/bashmakefileで、プロセス置換などのBASH固有の機能を有効にすることもできます。
ブレントブラッドバーン、2015年

8
微妙なポイント:後のスペース{{set、不明なコマンドとして解釈されないようにするために重要です。
ブレントブラッドバーン、2015

3
微妙なポイント#2:メイクファイルでは、それはおそらく問題ではないが、とラッピングの区別{}と、()あなたは時々スクリプトをコピーし、プロンプトシェルから直接それを実行したい場合は、大きな違いになります。変数を宣言し、特にset内で状態をで変更することにより、シェルインスタンスに大混乱をもたらすことができます{}()スクリプトが環境を変更するのを防ぎます。これはおそらく推奨されます。例(これはあなたのシェルセッションを終了します): { set -e ; } ; false
ブレントブラッドバーン、

次の形式を使用してコメントを含めることができます:command ; ## my comment \` (the comment is between ; `および` \ `)。コマンドを手動で(コピーアンドペーストで)実行した場合、コマンド履歴にコマンドが壊れるようにコメントが含まれることを除いてこれは正常に機能しているようです(再利用しようとした場合)。[注記:このコメントでは、バックティック内でバックスラッシュを使用しているため、構文の強調表示が壊れています。]
Brent Bradburn

makefile内のbash / perl / scriptの文字列を分割する場合は、文字列の引用符、バックスラッシュ、改行、(インデントなし)の開いている文字列の引用符を閉じます。例; perl -e QUOTE print 1; QUOTE BACKSLASH NEWLINE QUOTE print 2 QUOTE
mosh

2

コマンドを呼び出すだけの何が問題になっていますか?

foo:
       echo line1
       echo line2
       ....

2番目の質問では、代わりに$を使用してをエスケープする必要があります。$$bash -c '... echo $$a ...'

編集:あなたの例は次のような単一行のスクリプトに書き直すことができます:

gcc $(for i in `find`; do echo $i; done)

5
「すべてのコマンドは独自のシェルで実行される」ので、command1の結果をcommand2で使用する必要があります。例のように。
Jofsey 2012

シェル呼び出し間で情報を伝達する必要がある場合は、一時ファイルなどの何らかの外部ストレージを使用する必要があります。ただし、コードをいつでも書き換えて、同じシェルで複数のコマンドを実行できます。
JesperE 2012

+1、特に簡易バージョンの場合。そして、それは単に `` gcc `find```を実行することと同じではありませんか?
Eldar Abusalimov 2012

1
はい、そうです。しかし、もちろん「エコー」だけではなく、もっと複雑なこともできます。
JesperE 2012

2

もちろん、Makefileを作成する適切な方法は、どのターゲットがどのソースに依存しているかを実際に文書化することです。ささいなケースでは、提案されたソリューションはfooそれ自体に依存するようになりますが、もちろんmake循環依存を取り除くのに十分スマートです。ただし、一時ファイルをディレクトリに追加すると、「魔法のように」依存関係チェーンの一部になります。おそらくスクリプトを使用して、依存関係の明示的なリストを作成する方がよいでしょう。

GNU make gccは、一連の.cand .hファイルから実行可能ファイルを生成するための実行方法を知っているため、本当に必要なのは

foo: $(wildcard *.h) $(wildcard *.c)

1

ONESHELLディレクティブを使用すると、同じシェル呼び出しで実行される複数のラインレシピを記述できます。

SOURCE_FILES = $(shell find . -name '*.c')

.ONESHELL:
foo: ${SOURCE_FILES}
    for F in $^; do
        gcc -c $$F
    done

ただし、欠点もあります。特殊なプレフィックス文字(「@」、「-」、および「+」)の解釈は異なります。

https://www.gnu.org/software/make/manual/html_node/One-Shell.html

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.