轉載:(2條消息) makefile中SHELL變量的設置_洛奇看世界-CSDN博客_makefile shell變量
1. 問題的由來
這是以前遇到的一個問題,最近調試makefile想起來了,總結一下。
當時編譯一個公司早期發布的linux代碼,但在我Ubuntu 14.04上編譯的時候卻出現了一個錯誤:
cp -f defconfigs/defconfig-brcm-uclinux-rootfs-7400d0_be .config
rm -f linux-2.6.x/.config
(. .config; \
echo "cp -f vendors///config.linux-2.6.x /.config;"; \
cp -f vendors///config.linux-2.6.x /.config; \
)
/bin/sh: line 0: .: .config: file not found
make[2]: *** [vmlinuz-initrd-7400d0_be] Error 1
可以肯定的是這個代碼包在正式發布的時候一定是可以編譯的,所以我相信這是一個環境相關的問題,從錯誤信息看,實際上是用“.”去執行一個shell腳本時報錯。
2. 問題的復現
經簡化,可以用一個簡單的make工程來復現這個問題:
.PHONY: all clean
all: hello.sh
. hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
在這個makefile中先生成一個hello.sh的腳本(腳本只是簡單回顯一個“hello”字符串),然后再運行命令“. hello.sh”命令執行腳本,錯誤信息如下:
ygu@fs-ygu:/opt/work/make-example$ make
. hello.sh
/bin/sh: line 0: .: hello.sh: file not found
make: *** [all] Error 1
3. 幾個關於錯誤的實驗
在Ubuntu命令行用“. hello.sh”單獨執行是可以的:
ygu@fs-ygu:/opt/work/make-example$ . hello.sh
hello
a. 用“source”命令執行shell腳本
如果將“. hello.sh”修改為“source hello.sh”:
.PHONY: all clean
all: hello.sh
source hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
make時會報錯:
ygu@fs-ygu:/opt/work/make-example$ make
source hello.sh
make: source: Command not found
make: *** [all] Error 127
b. 直接執行shell腳本
i. 直接執行腳本
如果將“. hello.sh”修改為“./hello.sh”:
.PHONY: all clean
all: hello.sh
./hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
則make時報另外一個錯誤:
gu@fs-ygu:/opt/work/make-example$ make
./hello.sh
make: execvp: ./hello.sh: Permission denied
make: *** [all] Error 127
ii. 給腳本增加可執行權限
進一步修改,讓hello.sh具有可執行權限:
.PHONY: all clean
all: hello.sh
chmod a+x hello.sh
./hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
則可以正確運行:
ygu@fs-ygu:/opt/work/make-example$ make
chmod a+x hello.sh
./hello.sh
hello
c. makefile中SHELL的檢查
當前我的Ubuntu上默認執行的shell是/bin/bash,但makefile執行提示的是/bin/sh,可以在makefile中檢查SHELL的環境變量和當前makefile中的設定:
.PHONY: all clean
all: hello.sh
@echo 'Linux SHELL='$$SHELL
@echo ' Make SHELL='$(SHELL)
. hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
執行結果:
ygu@fs-ygu:/opt/work/make-example$ make
Linux SHELL=/bin/bash
Make SHELL=/bin/sh
. hello.sh
/bin/sh: line 0: .: hello.sh: file not found
make: *** [all] Error 1
從打印輸出的信息看,系統環境變量SHELL為/bin/bash,而makefile的變量SHELL確實為/bin/sh
d. 實驗結論
以上實驗說明腳本本身是沒有問題的,問題就在命令“. hello.sh”上。
4. make手冊的說明
查閱make手冊:https://www.gnu.org/software/make/manual/make.html
其中關於shell的設置,其中5.3.2節有如下描述:
5.3.2 Choosing the Shell
The program used as the shell is taken from the variable SHELL. If this variable is not set in your makefile, the program /bin/sh is used as the shell. The argument(s) passed to the shell are taken from the variable .SHELLFLAGS. The default value of .SHELLFLAGS is -c normally, or -ec in POSIX-conforming mode.
Unlike most variables, the variable SHELL is never set from the environment. This is because the SHELL environment variable is used to specify your personal choice of shell program for interactive use. It would be very bad for personal choices like this to affect the functioning of makefiles.
大意是說make使用SHELL變量來指定執行shell命令的程序,如果makefile中沒有設置SHELL變量,則默認使用/bin/sh。但是跟其他大多數變量不一樣的是SHELL變量並不從系統環境變量中繼承,這是因為系統環境變量“SHELL”用於指定哪個程序被用來作為用戶和系統交互的接口程序,他對於不存在交互過程的makefile是不合適的。
5. makefile中設置SHELL
通過在makefile中設置SHELL=/bin/bash,問題得到了解決:
SHELL=/bin/bash
.PHONY: all clean
all: hello.sh
. hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
makefile的執行:
ygu@fs-ygu:/opt/work/make-example$ make
. hello.sh
hello
執行的結果跟預期一致,問題得到了解決。
6. 關於/bin/sh和/bin/bash的疑問
現在還有一個問題是,在我當前Ubuntu 14.04的機器上,/bin/sh是軟鏈接到/bin/bash的,如下:
ygu@fs-ygu:/opt/work/make-example$ ls -l /bin
-rwxr-xr-x 1 root root 1021112 Oct 8 2014 bash
lrwxrwxrwx 1 root root 4 Aug 14 2014 sh -> bash
為什么/bin/bash可以正確執行而/bin/sh卻不能呢?
這就涉及到/bin/sh和/bin/bash的區別了,這里有一篇文章談到了這個問題:
《/bin/bash和/bin/sh的區別》: http://www.cnblogs.com/baizhantang/archive/2012/09/11/2680453.html
大體而言,現在的linux中sh一般設置為bash的軟鏈接,使用sh調用執行腳本相當於打開了bash的POSIX標准模式,也就是說 /bin/sh 相當於 /bin/bash –posix
我們可以修改makefile來驗證一下:
SHELL=/bin/bash --posix
#SHELL=/bin/bash
.PHONY: all clean
all: hello.sh
. hello.sh
clean:
rm -rf hello.sh
hello.sh:
@echo "#!/bin/bash" > hello.sh
@echo "" >> hello.sh
@echo "echo 'hello'" >> hello.sh
make時會報錯:
ygu@fs-ygu:/opt/work/make-example$ make
. hello.sh
/bin/bash: line 0: .: hello.sh: file not found
make: *** [all] Error 1
這個錯誤跟默認SHELL為/bin/sh的錯誤一樣。
7. 結語
以前在很多makefile中也看到過對SHELL的設置,但是沒看到哪里有引用,不清楚為什么要設置SHELL;
以前在很多make工程中沒有設置SHELL執行起來也好像沒有問題,所以一直也沒有發現SHELL問題;
建議:當執行makefile中的shell命令不成功時,先檢查下SHELL變量的設置。