コマンドの出力をMakefile変数に割り当てる方法


225

インストールされているPythonが特定のバージョン(2.5など)よりも新しい場合にのみ、条件付きでいくつかのmakeルールを実行する必要があります。

私は実行するような何かをすることができると思った:

python -c 'import sys; print int(sys.version_info >= (2,5))'

次に、ifeqmakeステートメントで出力(問題がない場合は「1」、それ以外の場合は「0」)を使用します。

単純なbashシェルスクリプトでは、次のようになります。

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

ただし、Makefileでは機能しません。

助言がありますか?他の賢明な回避策を使用してこれを達成することができます。


Makefileで私のために他のスクリプトを実行するためのコマンド作業の周りの奇妙な目盛り。他の何かかもしれません。
Leif Gruenwoldt 2014年

回答:


346

Make shellbuiltin in likeを使用しますMY_VAR=$(shell echo whatever)

me@Zack:~$make
MY_VAR IS whatever

me@Zack:~$ cat Makefile 
MY_VAR := $(shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)

34
shellは標準のMake組み込みコマンドではありません。これはGNU Make組み込みです。
デレクソン2014

12
stackoverflow.com/a/2373111/12916には、エスケープに関する重要な注意事項が追加されてい$ます。
Jesse Glick 2014

6
この単純な例は機能します。また、シェルコマンドパイプラインでも機能します。しかし、シェルコマンドで$を表すには$$を使用する必要があります
Sergey P. aka azure

28
質問は少し古いですが、MY_VAR:= $(shell ...)を実行するのが最善です。それ以外の場合、MY_VARが評価されるたびに、$(shell ...)が再度実行されます。
Russ Schultz

シェルと

29

課題をラップすることはeval私にとってはうまくいきます。

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)

6
「注:ここでの@trueにより、Makeは何も実行する必要がないとは考えなくなります。」ええと、それ.PHONY always make these targetsが目的です。
underscore_d

ありがとうございました!これは、メイクファイルの「奇妙なbash」をバイパスするのに役立ちます
Nam G VU

19

以下は、レシピ内のパイプと変数の割り当てを含む、もう少し複雑な例です。

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)

2
ケースでは、これまで私が取得するビット同様のアプローチを使用しています便利な来るPODNAME:展開の名前で$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
ヤン・リヒター

シェル組み込みの eval(docker-env行で)とmake関数 eval(次の行で)の両方を使用していることに気づくまで、構文によって少し混乱しました。
ブライアンゴードン

17

私は問題を解決する実際の構文の可視性を高めるために答えを書いています。残念ながら、誰かが取るに足りないと思うかもしれないことは、合理的な質問に対する単純な答えを探している人にとって非常に大きな頭痛の種になる可能性があります。

「Makefile」ファイルに以下を入れてください。

MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

確認したい動作は次のとおりです(最近のpythonがインストールされている場合)。

make
MY_VAR IS 1

上記のテキストをコピーしてMakefileに貼り付けると、これが表示されますか?おそらく違います。おそらくここで報告されているようなエラーが発生します:

makefile:4:***セパレータがありません。やめる

理由:私は本物のタブを個人的に使用しましたが、スタックオーバーフロー(役立つようにしようとする)が私のタブをいくつかのスペースに変換します。欲求不満のインターネット市民であるあなたは、これをコピーし、私が使用したのと同じテキストを持っていると思います。makeコマンドはスペースを読み取り、「all」コマンドの形式が正しくないことがわかりました。上記のテキストをコピーして貼り付け、 "@ echo"の前の空白をタブに変換すると、この例がようやくうまくいくと思います。


貼り付け先のエディターによって異なります。私はEclipse Makefileエディターにコピーして貼り付けたところ、必要に応じて先頭のタブが表示されました。
Technophile

ああ、そうは思いませんでした。ここにアトム。
AlanSE

11

このようなレシピに注意してください

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

それは2つのことを間違っています。レシピの最初の行は、2番目の行とは別のシェルインスタンスで実行されます。その間、変数は失われます。2つ目の誤りは、$エスケープされていないことです。

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

両方の問題が修正され、変数は使用可能です。バックスラッシュは両方の行を組み合わせて1つのシェルで実行するため、変数の設定と変数の後書きの読み取りが機能します。

元の投稿では、シェルコマンドの結果をMAKE変数に取得する方法を説明していることを理解しています。この回答は、シェルコマンドに結果を取得する方法を示しています。しかし、他の読者にもメリットがあるかもしれません。

最後の1つの改善点は、消費者が「環境変数」が設定されることを期待している場合は、それをエクスポートする必要があることです。

my_shell_script
    echo $MY_ID

メイクファイルでこれが必要になります

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_shell_script;

それが誰かを助けることを願っています。一般に、レシピ以外で実際の作業を行わないようにする必要があります。誰かが '--dry-run'オプションを指定してmakefileを使用すると、それが何をするのかを確認するだけで、望ましくない副作用が発生しないためです。すべての$(shell)呼び出しはコンパイル時に評価され、実際の作業の一部が誤って実行される可能性があります。可能な場合は、IDの生成などの実際の作業をレシピの内部に残した方がよいでしょう。


9

GNU Makeでは、shellおよびevalを使用して、任意のコマンドライン呼び出しからの出力を保存、実行、および割り当てることができます。以下の例と使用例との違い:=は、:=割り当てが1回(発生したときに)発生することです。で設定された再帰的に展開された変数=は、もう少し「遅延」です。他の変数への参照は、変数自体が参照されるまで残り、変数が参照されるたびに後続の再帰的展開が行われます。これは、「一貫性のある呼び出し可能なスニペット」を作成するために望ましいことです。詳細については、変数の設定に関するマニュアルを参照しください。

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)

私が出会った最初の素晴らしい説明があります。ありがとうございました。追加すべきことの1つは、false$(SET_ID)であるif句の内部にある場合、それはまだ呼び出されるということです。
ローマ

if stmtsが実行時ではなくメイクファイルのコンパイル時に評価されるので、それはまだ呼び出されます。ランタイム固有の指示については、それらをレシピに入れてください。それらが条件付きである場合、レシピの一部として、bash /シェルで書かれたif stmtsを追加します。
ジュラジ

0

以下の例では、Makefileフォルダーのパスを保存し、ターゲットで変数をLOCAL_PKG_DIR使用していLOCAL_PKG_DIRます。

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

.PHONY: print
print:
    @echo $(LOCAL_PKG_DIR)

端末出力:

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