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

makeで大量のメッセージが表示されるときは-dオプションを疑う もしくは変数展開についてのbash/zshの違い

linux zsh

このエントリーをはてなブックマークに追加

ある私的プロジェクトでMakefileの変数$(CXXFLAGS)を変更して実行するために、以下のようなスクリプト script.sh

CXXFLAGS="-std=c++0x -stdlib=libc++"
make CXXFLAGS=${CXXFLAGS}

を書きました。このスクリプトの意図は、Makefileに記されたCXXFLAGSという変数を、"-std=c++0x -stdlib=libc++"という文字列で上書きして、Makeを実行することです。

このスクリプトを、bashで実行します。

$ bash script.sh

すると、以下のような数百行にも渡る大量のメッセージが表示されました。

GNU Make 4.1
このプログラムは i686-pc-cygwin 用にビルドされました
Copyright (C) 1988-2014 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリーソフトウェアです: 自由に変更および配布できます.
法律の許す限り、 無保証 です.
makefile を読み込みます...
makefile 'Makefile' の読み込み中...
makefile の更新中....
 ファイル 'Makefile' を検討しています.
  'Makefile' のための暗黙ルールを探します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.o' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.c' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.cc' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.C' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.cpp' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.p' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.f' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.F' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.m' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.r' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.s' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.S' を試します.
  語幹 'Makefile' とのパターンルールを試します.
  暗黙の必要条件 'Makefile.mod' を試します.

(以下400行ほど続く)

   語幹 'test_constructor.cpp' とのパターンルールを試します.
   暗黙の必要条件 'MyVectorTest/SCCS/s.test_constructor.cpp' を試します.
   'MyVectorTest/test_constructor.cpp' のための暗黙ルールがありません.
   ターゲットファイル 'MyVectorTest/test_constructor.cpp' の必要条件を満たしました.
  ターゲット 'MyVectorTest/test_constructor.cpp' を再make する必要はありません.
 ターゲットファイル 'test' の必要条件を満たしました.
ターゲット 'test' を再make する必要があります.
ターゲットファイル 'test' の再 make に成功しました.

賢明な皆様はお気づきかもしれませんが、原因はbashの引数の展開方法にあります。

make CXXFLAGS=${CXXFLAGS}

の部分が

make CXXFLAGS=-std=c++0x -stdlib=libc++

と展開されてしまったおかげで、CXXFLAGSに代入される値が -std=c++0x だけになってしまいました。 残りの-stdlibの部分で、makeに-dオプションを渡したと誤解されてしまい、先ほどのような冗長なデバッグ情報が表示されてしまったようです。

初めの意図通りの処理をさせるには、変数の前後をクオートで囲むのが正解でした。

CXXFLAGS="-std=c++0x -stdlib=libc++"
make CXXFLAGS="${CXXFLAGS}"

bashスクリプトでのクオートについては Quoting Variables が詳しいです。以下のスクリプト

List="one two three"

for a in $List
do
  echo "$a"
done

echo "---"

for a in "$List"
do
  echo "$a"
done

を実行すると、クオートの有無による動きの違いがよくわかります。上記をshやbashで実行すると、

one
two
three
---
one two three

と表示されます。

なお、ややこしいことに、私の手元のzshでこのスクリプトを実行すると、

one two three
---
one two three

と、クオートがあろうがなかろうが同じ結果になってしまいました。shell - Variable expansion is different in zsh from that in bash - Stack Overflow によると、これがzshのデフォルトの振る舞いのようです。

zshでもbashと同じルールで変数展開したい場合は、

ls ${=args}

とするか

setopt SH_WORD_SPLIT

とすればよいようです(未確認)。