読者です 読者をやめる 読者になる 読者になる

SIerだけど技術やりたいブログ

5年目のSIerのブログです

makeのshell関数の実行タイミングは直感と異なる

makeのshell関数の実行タイミングは直感と異なる

Makefile作成時にshell関数の実行タイミングが直感と異なっていてハマったのでメモ。

やりたかったこと

これ。
www.rhoboro.com

sphinxドキュメントをビルドするときに、なるべくビルド環境を汚さず、CIツールへの依存も減らしたかった。

想定する流れ。

  • makeでDockerをビルド・実行しsphinxドキュメントを作成(docker build ,docker run)
  • コンテナに生成された成果物をローカルに持ってくる(docker cp)
  • コンテナを削除(docker rm)

このとき、コンテナの削除を以下のコマンドで削除できるかと思ったらできず。

build:
        docker build -t xxx .
        docker run --name yyy xxx 
        ...
        docker rm $(shell docker ps -aqf name=yyy)

以下のように $(shell docker ps -aqf name=yyy) の実行結果が空になる。なぜ…。

docker rm
docker: "rm" requires a minimum of 1 argument.
See '/usr/bin/docker-current rm --help'.

Usage:  docker rm [OPTIONS] CONTAINER [CONTAINER...]

Remove one or more containers
make: *** [build] エラー 1

原因

上から順に処理が実行されるのではなく、まず初めにshell関数が実行される。

例えば以下の場合、

task:
        touch sample.txt
        echo $(shell ls)
        rm sample.txt

上から順にコマンドが実行されないので、echo $(shell ls) の結果にsample.txtが含まれない。

$ make
touch sample.txt
echo Makefile shell.sh
Makefile shell.sh
rm sample.txt

シェルスクリプトの場合

シェルで別コマンドの実行結果を利用するといえばコマンド置換($(),``)。コマンド置換はそのコマンドが書かれた行で実行される。

#!/bin/sh

touch sample.txt
echo $(ls)
rm sample.txt

上から順にコマンドが実行されるので、 echo $(ls) の結果にsample.txtが含まれる。

$ ./shell.sh
Makefile sample.txt shell.sh

解決策

makeでlinuxと同じような順序で実行したければ、シェルのコマンド置換を利用する。
そのためには、$をエスケープすればいい。

task:
        touch sample.txt
        echo $$(ls)
        rm sample.txt
$ make
touch sample.txt
echo $(ls)
Makefile sample.txt shell.sh
rm sample.txt

はーもう悩むのめんどくさいし、シェルでいいものはシェルで書くことにする。