為什麼 echo -e 在 Makefile 中表現得很奇怪?
我正在編寫一個 Makefile(在 Ubuntu 20.04 上,如果相關的話)並註意到
echo
. 使用這個簡單的 Makefile:test.txt: @echo -e 'hello\nworld' @echo -e 'hello\nworld' > test.txt
當我執行時
make
,我希望在 stdout 上看到與在 test.txt 中相同的內容,但實際上我沒有。我在標準輸出上得到了這個:hello world
但這在 test.txt 中:
-e hello world
同時,如果我
-e
從 Makefile 的兩行中刪除,我會在標準輸出上得到這個:hello\nworld
這在 test.txt 中:
hello world
這讓我想知道是否
echo
檢測到重定向並且行為不同,但是當我只是在 shell 中手動執行它時它不會/bin/echo -e 'hello\nworld' > test.txt
(這會產生hello
和world
在單獨的行上,正如我通常所期望的那樣)。@echo --version
我什至通過添加一行來確認 Makefile 正在使用 /bin/echo 而不是內置的 shell 。這裡發生了什麼?
的 UNIX 兼容實現
echo
需要在-e<space>hello<newline>world<newline>
那裡輸出。那些不符合的。許多不是,這意味著它幾乎不可能
echo
便攜使用,printf
應該改為使用。bash
’secho
,在它的某些(大多數)版本中,僅當您同時啟用posix
和xpg_echo
選項時才符合要求。這可能是echo
您所期望的行為。
echo
GNU 附帶的獨立實用程序也是如此,該實用程序只有在其環境中coreutils
使用 set 呼叫時才符合要求$POSIXLY_CORRECT
(並且是足夠新的版本)。
make
通常執行sh
以解釋每個操作行上的命令行。
make
但是,作為優化,如果程式碼足夠簡單並且它認為不需要呼叫 shell 來解釋它,則 GNU 實現可以直接執行命令。這就解釋了為什麼
echo --version
給你/bin/echo
,但echo ... > file
需要一個 shell 來執行重定向。您可以使用
strace -fe execve make
查看make
執行的內容(或truss
/tusc
… 如果不是 Linux,則在您的系統上等效)。在這裡,似乎雖然您
/bin/echo
不合規,但您sh
的echo
內置函式是合規的。在這裡,
printf
如果要擴展echo
-style 轉義序列,請使用:printf '%b\n' 'hello\nworld'
在其格式參數中,理解 C 風格的轉義序列( (echo) 與(C) 八進制序列
printf
的 echo 風格的轉義序列有所不同)\0xxx``\xxx
printf 'hello\nworld\n'
在這裡,您還可以這樣做:
printf '%s\n' hello world
這是在單獨的行上輸出多個參數的常見且可移植的方式。
另一種方法是添加:
SHELL = bash
Makefile
供您make
呼叫bash
(假設它已安裝並在 中找到$PATH
)而不是sh
解釋命令行。或呼叫make
asmake <target> SHELL=bash
。這並不一定會讓它更便攜,因為現在有越來越多的系統
bash
預設安裝,有一些(比如在 Solaris 上)bash
是建構的,因此它的echo
內置預設行為是標準方式。設置為除了上面提到的禁用 GNU優化
SHELL
之外的任何其他值,因此使用or ,您將在兩次呼叫之間獲得一致的行為,同時仍然不必向 bash 添加依賴項。/bin/sh``make``make <target> SHELL=sh``make <target> SHELL=/bin//sh