2009-11-24 97 views
227

例如,我在我的Makefile是這樣的:如何在makefile中寫入'cd'命令?

all: 
    cd some_directory 

但是,當我輸入make我看到的只是「CD some_directory」,像在echo命令。

+3

目前還不清楚你想做什麼,但是,根據我對'make'的經驗,我從來不想改變這個目錄。也許你應該嘗試另一種解決方案? –

+0

相信你的目錄很重要,這是一個常見的新手錯誤。對於大多數情況來說,它不是; 'cd dir; cmd文件幾乎總是可以用'cmd dir/file'來表示。 – tripleee

+8

這是一個常見的新手錯誤,相信你目前的工作目錄是無足輕重的。許多程序,特別是shell腳本,都是以「。」爲特定的值寫入的。 的確,大多數工具都是這樣設計的,你不需要爲它改變你的密碼。但這並非總是如此,我認爲將其稱爲「錯誤」以相信您的目錄可能很重要是個好主意。 –

回答

408

它實際上正在執行命令,將目錄更改爲some_directory,但是,這是在子進程shell中執行的,並且不影響make和您正在使用的shell。

如果您要在some_directory範圍內執行更多任務,則需要添加一個分號並附加其他命令。請注意,您不能使用換行符,因爲它們被make解釋爲規則的結尾,因此爲清晰起見使用的任何換行符都需要使用反斜槓進行轉義。

例如:

all: 
     cd some_dir; echo "I'm in some_dir"; \ 
      gcc -Wall -o myTest myTest.c 

還要注意分號是每一個命令之間必要的,即使你加一個反斜槓和一個換行符。這是由於整個字符串被shell解析爲一行。正如評論中指出的那樣,您應該使用'& &'來加入命令,這意味着只有在前面的命令成功時纔會執行它們。

all: 
     cd some_dir && echo "I'm in some_dir" && \ 
      gcc -Wall -o myTest myTest.c 

做破壞性的工作,如清理時,你會否則將會破壞錯了東西,應cd失敗不管出於什麼原因,這一點尤爲重要。

雖然常用的方法是在子目錄中調用make,但您可能需要查看該子目錄。有此一命令行選項,這樣你就不用調用cd自己,所以你的規則是這樣的

all: 
     $(MAKE) -C some_dir all 

這將改變成some_dir,並在那裏與目標「的所有」執行Makefile。作爲最佳實踐,使用$(MAKE)而不是直接調用make,因爲它會小心地調用正確的make實例(例如,如果您爲構建環境使用特殊的make版本),並提供稍微不同的行爲運行時使用某些開關,如-t

對於記錄,使總是回聲它執行的命令(除非明確禁止),即使它沒有輸出,這就是你所看到的。

+6

那麼,不*總是*。爲了抑制回聲,只需在該行的開頭添加@。 – Beta

+1

@Beta:好的,並且破折號前綴也會忽略錯誤狀態。也許我有點失落了,我想指出這樣一個事實,即無論命令是什麼,命令都會迴應命令。在這種情況下,這是一個沒有輸出的命令,這讓不熟悉make的人看起來更加陌生。 – falstro

+33

兩個nits:1.這些命令應該真的被'&&'連接,因爲用';'如果目錄不存在並且'cd'失敗,shell將繼續運行當前目錄中的其餘命令,這可能會導致諸如編譯時發現神祕的「文件未找到」消息,調用make時出現無限循環,或者像'clean :: cd dir; rm -rf *'。 2.調用子make時,調用'$(MAKE)'而不是'make',以便[選項將正確傳遞](http://www.gnu.org/software/make/manual/make.html #MAKE-變量)。 – andrewdotn

5

一旦到達那裏,你想要它做什麼?每個命令都在子shell中執行,因此子shell會更改目錄,但最終結果是下一個命令仍在當前目錄中。

用GNU做,你可以這樣做:

BIN=/bin 
foo: 
    $(shell cd $(BIN); ls) 
+3

爲什麼'$(shell ...)'時出現cd $(BIN); ls'或'cd $(BIN)&& ls'(就像@andrewdotn指出的)就足夠了。 – vapace

20

這裏有一個可愛的把戲對付目錄而作。而不是使用多行字符串,或「cd;」每個命令,定義了一個簡單的chdir函數像這樣:

CHDIR_SHELL := $(SHELL) 
define chdir 
    $(eval _D=$(firstword $(1) $(@D))) 
    $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL)) 
endef 

然後,所有你需要做的就是把它在你的規則,像這樣:

all: 
      $(call chdir,some_dir) 
      echo "I'm now always in some_dir" 
      gcc -Wall -o myTest myTest.c 

你甚至可以做到以下幾點:

some_dir/myTest: 
      $(call chdir) 
      echo "I'm now always in some_dir" 
      gcc -Wall -o myTest myTest.c 
+32

「可愛」?更像是有足夠的繩子在腳下射擊自己。 – tripleee

+0

這個當前目​​錄是爲那些只在該規則中的命令設置的,還是爲所有後來執行的規則設置的?此外,將在Windows下這項工作的一些變化? – user117529

+4

這當然會破壞並行執行('-jn'),這是_make_的全部重點。 – bobbogo

36

GNU make 3.82開始,您可以使用.ONESHELL特殊目標在外殼的單實例運行所有食譜線:

  • 新的特殊目標:.ONESHELL指示作出調用單個實例的外殼 並提供其與整個配方,不管它有多少 線包含的內容。 [...]

例子:

.ONESHELL: 
all: 
    cd ~/some_dir 
    pwd # this line is correctly run in ~/some_dir if cd succeeded 
+4

請注意,'pwd'本身以及'''pwd'''(帶反引號),但'$(shell pwd)'和'$(PWD)'在執行'cd之前仍然會返回目錄'命令,所以你不能直接使用它們。 – anol

+2

是的,因爲在通過make執行命令之前完成了變量和函數的擴展,而'pwd'和'''pwd \''是由shell本身執行的。 – Chnossos

+0

是的,但沒有人可以安裝3.82,因爲它引入了破解兼容性變化:( – CpILL

1

像這樣:

target: 
    $(shell cd ....); \ 
    # ... commands execution in this directory 
    # ... no need to go back (using "cd -" or so) 
    # ... next target will be automatically in prev dir 

祝你好運!