2009-11-24 8 views
227

Ví dụ, tôi có một cái gì đó như thế này trong makefile của tôi:Làm cách nào để viết lệnh 'cd' trong một tệp makefile?

all: 
    cd some_directory 

Nhưng khi tôi gõ make tôi thấy chỉ 'cd some_directory', giống như trong lệnh echo.

+3

Không rõ bạn muốn làm gì, nhưng, theo kinh nghiệm của tôi với 'make', tôi không bao giờ muốn thay đổi thư mục như thế này. Có lẽ bạn nên thử một cách tiếp cận khác để giải pháp của bạn? –

+0

Đó là lỗi thường gặp của người mới khi tin rằng thư mục của bạn là quan trọng. Đối với hầu hết mọi thứ nó không phải là; 'cd dir; cmd file' có thể gần như luôn được thể hiện một cách hữu ích hơn như là 'cmd dir/file'. – tripleee

+8

Đó là một sai lầm newbie phổ biến để tin rằng thư mục làm việc hiện tại của bạn là không quan trọng. Nhiều chương trình, đặc biệt là các kịch bản shell, được viết với một giá trị cụ thể của '.' trong tâm trí. Đúng là hầu hết các công cụ được thiết kế theo cách mà bạn không cần phải thay đổi pwd của bạn. Nhưng điều này không phải lúc nào cũng đúng, và tôi không nghĩ đó là một ý hay khi gọi đó là một "sai lầm" để tin rằng thư mục của bạn có thể quan trọng. –

Trả lời

408

Thực tế là thực thi lệnh, thay đổi thư mục thành some_directory, tuy nhiên, điều này được thực hiện trong trình vỏ phụ và không ảnh hưởng đến việc tạo cũng như vỏ mà bạn đang làm việc.

Nếu bạn đang muốn thực hiện nhiều tác vụ hơn trong some_directory, bạn cũng cần phải thêm dấu chấm phẩy và nối thêm các lệnh khác. Lưu ý rằng bạn không thể sử dụng dòng mới vì chúng được diễn giải bằng cách thực hiện như là kết thúc của quy tắc, do đó, bất kỳ dòng mới nào bạn sử dụng để rõ ràng cần được thoát bằng dấu gạch chéo ngược.

Ví dụ:

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

cũng Lưu ý rằng các dấu chấm phẩy là cần thiết giữa mỗi lệnh ngay cả khi bạn thêm một dấu chéo ngược và một dòng mới. Điều này là do thực tế rằng toàn bộ chuỗi được phân tích cú pháp dưới dạng một dòng duy nhất của trình bao. Như đã lưu ý trong các nhận xét, bạn nên sử dụng '& &' để tham gia các lệnh, có nghĩa là chúng chỉ được thực thi nếu lệnh trước đó thành công.

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

Điều này đặc biệt quan trọng khi thực hiện công việc phá hoại, chẳng hạn như dọn dẹp, như bạn sẽ khác phá hủy những thứ sai, nên các cd thất bại vì lý do gì.

Một cách sử dụng phổ biến là gọi điện trong thư mục con mà bạn có thể muốn xem xét. Có một tùy chọn dòng lệnh cho điều này, do đó bạn không cần phải gọi cd mình, vì vậy quy tắc của bạn sẽ trông như thế này

all: 
     $(MAKE) -C some_dir all 

mà sẽ thay đổi vào some_dir và thực hiện Makefile ở đó với mục tiêu "tất cả". Để thực hành tốt nhất, hãy sử dụng $(MAKE) thay vì gọi trực tiếp make vì sẽ cẩn thận khi gọi đúng ví dụ (ví dụ: bạn sử dụng phiên bản tạo đặc biệt cho môi trường xây dựng), cũng như cung cấp hành vi hơi khác khi chạy bằng các công tắc nhất định, chẳng hạn như -t.

Để lưu bản ghi, hãy thực hiện luôn lặp lại lệnh mà nó thực thi (trừ khi bị triệt tiêu một cách rõ ràng), ngay cả khi nó không có đầu ra, đó là những gì bạn đang thấy.

+6

Vâng, không phải * luôn luôn *. Để chặn tiếng vang, chỉ cần đặt @ ở đầu dòng. – Beta

+1

@Beta: vâng, và tiền tố dấu gạch ngang cũng bỏ qua trạng thái lỗi. Có lẽ tôi đã có một chút mang đi, tôi muốn chỉ ra một thực tế là làm cho echos lệnh, bất kể loại lệnh đó là gì. Và trong trường hợp này, đó là một lệnh không có đầu ra, khiến cho việc lặp lại có vẻ xa lạ đối với một người không quen thuộc với việc tạo ra. – falstro

+33

Hai nits: 1. Các lệnh thực sự nên được nối bởi '&&', bởi vì với ';' nếu thư mục không tồn tại và 'cd' thất bại, shell sẽ tiếp tục chạy phần còn lại của các lệnh trong thư mục hiện tại , có thể gây ra những thứ như thông điệp "không tìm thấy tệp" bí ẩn cho các biên dịch, vòng lặp vô hạn khi gọi thực hiện hoặc thiên tai cho các quy tắc như 'clean :: cd dir; rm -rf * '. 2. Khi gọi các phần phụ, hãy gọi '$ (MAKE)' thay vì 'make' để [tùy chọn sẽ được truyền vào đúng] (http://www.gnu.org/software/make/manual/make.html # MAKE-Variable). – andrewdotn

5

Bạn muốn làm điều gì khi nó đến đó? Mỗi lệnh được thực hiện trong một subshell, do đó, thư mục thay đổi subshell, nhưng kết quả cuối cùng là lệnh tiếp theo vẫn còn trong thư mục hiện hành.

Với GNU thực hiện, bạn có thể làm một cái gì đó như:

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

Tại sao '$ (shell ...)' khi 'cd $ (BIN); ls' hoặc 'cd $ (BIN) && ls' (như @andrewdotn chỉ ra) sẽ là đủ. – vapace

20

Dưới đây là một thủ thuật dễ thương để đối phó với các thư mục và thực hiện.Thay vì sử dụng chuỗi nhiều dòng hoặc "cd;" trên mỗi lệnh, xác định một hàm chdir đơn giản như vậy:

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

Sau đó, tất cả các bạn phải làm là gọi nó trong quy tắc của bạn như vậy:

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

Bạn thậm chí có thể làm như sau:

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

"Dễ thương"? Giống như dây thừng đủ để bắn mình vào chân. – tripleee

+0

Thư mục hiện tại này có được đặt cho các lệnh chỉ trong quy tắc đó hoặc cho tất cả các quy tắc được thực hiện sau đó không? Ngoài ra, sẽ có một số biến thể của công việc này trong Windows? – user117529

+4

Điều này tất nhiên phá vỡ cho thực hiện song song ('-jn'), đó là toàn bộ điểm của _make_ thực sự. – bobbogo

36

Bắt đầu từ GNU make 3.82, bạn có thể sử dụng các mục tiêu đặc biệt .ONESHELL để chạy tất cả các dòng công thức trong một instantiation duy nhất của shell:

  • mục tiêu đặc biệt mới: .ONESHELL chỉ thị thực hiện để gọi một trường hợp duy nhất của vỏ và cung cấp nó với toàn bộ công thức, bất kể có bao nhiêu dòng nó chứa. [...]

Ví dụ:

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

Chỉ cần lưu ý rằng 'pwd' tự nó hoạt động, cũng như' \ 'pwd \' '(với các dấu sau), nhưng' $ (shell pwd) 'và' $ (PWD) 'sẽ vẫn trả về thư mục trước khi thực hiện' cd 'lệnh, vì vậy bạn không thể sử dụng chúng trực tiếp. – anol

+2

Có vì biến và mở rộng chức năng được thực hiện trước khi thực hiện các lệnh bằng cách thực hiện, trong khi 'pwd' và' \ 'pwd \' 'được thực thi bởi chính shell đó. – Chnossos

+0

vâng, nhưng không ai có thể cài đặt 3.82 vì nó giới thiệu các thay đổi tương thích phá vỡ: ( – CpILL

1

Như thế này:

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 

Chúc may mắn!