Makefileでシェルコマンドを使用する方法


99

ls他のコマンド(echo、rsyncなど)で結果を使用しようとしています:

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

しかし、私は得ます:

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

私が使ってみたことecho $$FILESecho ${FILES}およびecho $(FILES)、運を持ちます。

回答:


149

と:

FILES = $(shell ls)

そのallようにインデントされている、それはビルドコマンドです。したがって、これは展開して$(shell ls)からコマンドを実行しようとしますFILES ...

場合FILESことになっているmake変数、これらの変数は、レシピの部分、例えば外に割り当てる必要があります。

FILES = $(shell ls)
all:
        echo $(FILES)

もちろん、その手段FILES「からの出力に設定されますlsの.tgzファイルを作成するコマンドのいずれかを実行しています。(Kazは変数が毎回再展開されることに注意しているので、最終的には.tgzファイルが含まれます。一部のmakeバリアントはFILES := ...、効率や正確さのためにこれを回避する必要があります。1

FILESがシェル変数であると想定されている場合は設定できますが、スペースなしで引用符で囲んでshell-eseで行う必要があります。

all:
        FILES="$(shell ls)"

ただし、各行は個別のシェルによって実行されるため、この変数は次の行まで存続しないため、すぐに使用する必要があります。

        FILES="$(shell ls)"; echo $$FILES

シェルが*最初に展開するので(そして他のシェルglob式も)、これは少しばかげています。

        echo *

あなたのシェルコマンドとして。

最後に、一般的な規則として(この例には実際には適用されません):コメントのエスペラントの注釈として、からの出力の使用はls完全に信頼できません(一部の詳細はファイル名に依存し、時にはのバージョンにも依存しますlsls出力のサニタイズを試みるいくつかのバージョンある場合には)。したがって、l0b0イデリックノートとして、GNU makeを使用$(wildcard)$(subst ...)ている場合は、makeそれ自体を使用してすべてを実行できます(「ファイル名の奇妙な文字」の問題を回避します)。(shメイクファイルのレシピ部分を含むスクリプトでは、もう1つの方法は、find ... -print0 | xargs -0空白、改行、制御文字などのつまずきを回避するために使用することです。)


1 GNU Makeのドキュメントには、POSIX make ::=が2012年に割り当てを追加したことがさらに記載されています。このためのPOSIXドキュメントへのクイックリファレンスリンクは見つかりません。また、割り当てがmakeサポートされているバリアントもすぐにはわかりません。::=:=

私が知る限り、すべての最新のGNUおよびBSDバリアントを含む、いくつかのバリアントでVAR := $(shell command args...)も綴られることに注意してください。これらの他の変形はありませんので、使用して両方が短いことで優れているより多くの変種で働きます。VAR != command args...make$(shell)VAR != command args...


ありがとう。精巧なコマンド(lswith sedやcutなど)を使用し、その結果をrsyncやその他のコマンドで使用したいと思います。長いコマンドを何度も繰り返す必要がありますか?結果を内部のMake変数に保存できませんか?
Adam Matan

1
Gnu makeはそれを行う方法があるかもしれませんが、私はそれを使用したことがありません。使用するすべての恐ろしく複雑なmakefileは、シェル変数と、各行の最後に「; \」で構築された巨大な1行シェルコマンドを使用するだけです。必要。(ここでは、バックスラッシュシーケンスで動作するコードエンコーディングを取得できません、hmm)
torek

1
おそらく何かのように: FILE = $(shell ls *.c | sed -e "s^fun^bun^g")
ウィリアム・モリス

2
@William:makeシェルを使用せずにそれを行うことができます:FILE = $(subst fun,bun,$(wildcard *.c))
Idelic

1
この場合はそれほど重要ではないように見えますが、lsの出力を自動的に解析するべきではないことを指摘しておきます。lsは、人間に情報を表示することを目的としており、スクリプトにチェーンされることはありません。詳細はこちら:mywiki.wooledge.org/ParsingLs おそらく、 'make'が適切なワイルドカード拡張を提供しない場合、 "find"は "ls"よりも優れています。
ラウル・サリナス- Monteagudo

53

また、トレックの答えに加えて、1つ目立っているのは、遅延評価されたマクロ割り当てを使用していることです。

GNU Makeを使用している場合は、の:=代わりに割り当てを使用してください=。この割り当てにより、右側がすぐに展開され、左側の変数に格納されます。

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

=割り当てを使用する場合は、$(FILES)が出現するたびに$(shell ...)構文が拡張され、シェルコマンドが呼び出されます。これにより、makeジョブの実行が遅くなったり、驚くべき結果が生じたりします。


1
リストが作成されたので、リスト内の各アイテムをどのように反復処理し、そのコマンドを実行するのですか ビルドやテストなど?
anon58192932

3
@ anon58192932コマンドを実行するための特定の反復は通常、ビルドレシピのシェル構文フラグメントで行われるため、makeではなくシェルで発生します for x in $(FILES); do command $$x; done。シェルに$$シングル$を渡すダブルアップに注意してください。また、シェルフラグメントはワンライナーです。複数行のシェルコードを作成するには、バックスラッシュの継続を使用します。これは、make単独で処理され、1行に折り返されます。つまり、シェルコマンドを区切るセミコロンは必須です。
Kaz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.