Làm cách nào để tôi viết lệnh 'cd' trong tệp thực hiện?


354

Ví dụ, tôi có một cái gì đó như thế này trong tệp tạo tệp của mình:

all:
     cd some_directory

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


5
Không rõ bạn muốn làm gì, nhưng theo kinh nghiệm của tô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 cho giải pháp của bạn?
P Shved

2
Đó là một lỗi người mới phổ biến để 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 filegần như luôn luôn có thể được thể hiện hữu ích hơn như cmd dir/file.
tripleee

34
Đó là một lỗi người mới 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ể .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 mình cho nó. Nhưng điều này không phải lúc nào cũng đúng và tôi không nghĩ nên gọi đó là "sai lầm" để tin rằng thư mục của bạn có thể quan trọng.
Evelyn Kokemoor 28/03/2016

Mẹo: nếu lệnh cd của bạn nói "Không có tệp hoặc thư mục như vậy", ngay cả khi thư mục (tương đối) không tồn tại, hãy kiểm tra xem biến môi trường CDPATH của bạn có trống hay bao gồm ".". Thực hiện các lệnh bằng "sh", sẽ chỉ tìm thấy một đường dẫn tương đối thông qua CDPATH nếu nó được đặt. Điều này tương phản với bash, sẽ cố gắng. trước khi tư vấn CDPATH.
Denis Howe

Câu trả lời:


620

Nó thực sự đang 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 bao quy trình phụ và ảnh hưởng đến cả trình tạo mà bạn không làm việc.

Nếu bạn đang tìm cách thực hiện nhiều tác vụ bên trong some_directory, bạn cần 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 các dòng mới vì chúng được giải thích bằng cách kết thúc quy tắc, do đó, bất kỳ dòng mới nào bạn sử dụng cho sự rõ ràng cần phải được thoát khỏi 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 dấu chấm phẩy là cần thiết giữa mỗi lệnh mặc dù bạn thêm dấu gạch chéo ngược và dòng mới. Điều này là do thực tế là toàn bộ chuỗi được phân tách thành một dòng duy nhất bởi shell. 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, vì nếu không bạn sẽ phá hủy những thứ sai, nếu cdthất bại vì bất kỳ lý do gì.

Một cách sử dụng phổ biến là gọi make 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 việc này để bạn không phải tự gọi cdmình, vì vậy quy tắc của bạn sẽ giống như thế này

all:
        $(MAKE) -C some_dir all

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

Đối với bản ghi, hãy luôn tạo ra tiếng vang cho lệnh mà nó thực thi (trừ khi bị triệt tiêu rõ ràng), ngay cả khi nó không có đầu ra, đó là những gì bạn đang thấy.


8
Vâng, không phải lúc nào . Để triệt tiêu tiếng vang, chỉ cần đặt @ ở đầu dòng.
Beta

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

46
Hai nits: 1. Các lệnh nên thực sự được nối bởi &&;nếu thư mục không tồn tại và cdbị lỗ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, điều này có thể gây ra những thứ như tập tin bí ẩn đã tìm thấy các thông báo của người dùng cho các trình biên dịch, các vòng lặp vô hạn khi gọi make hoặc thảm họa cho các quy tắc như clean:: cd dir; rm -rf *. 2. Khi gọi phụ, hãy gọi $(MAKE)thay vì makevậy các tùy chọn sẽ được chuyển chính xác .
andrewdotn

2
@perreal, tôi thường định nghĩa một quy tắc mẫu như vậy: %-recursive:với phần thân: @T="$@";$(MAKE) -C some_dir $${T%-*}(Tôi cũng thường có một vòng lặp for, để lặp qua danh sách các thư mục con, phần $${T%-*}mở rộng bash sẽ loại bỏ -recursivemột phần của tên đích) và sau đó xác định rõ ràng ngắn tay (và .PHONY) mục tiêu cho mỗi, như all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro

1
@ChristianStewart, đúng, như đã được đề cập trong nhận xét 2 và 3.
falstro

94

Bắt đầu từ GNU make 3.82 (tháng 7 năm 2010), bạn có thể sử dụng .ONESHELLmục tiêu đặc biệt để chạy tất cả các dòng công thức trong một lần khởi tạo vỏ (khai thác đậm nhấn mạnh):

  • Mục tiêu đặc biệt mới: .ONESHELLhướng dẫn thực hiện để gọi một thể hiện duy nhất của shell và cung cấp cho nó toàn bộ công thức , bất kể nó chứa bao nhiêu dòng. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
Chỉ cần lưu ý rằng pwdbản thân công trình, cũng như `pwd`(với backticks), nhưng $(shell pwd)$(PWD)vẫn sẽ trở lại thư mục trước khi thực hiện cdlệnh, vì vậy bạn không thể sử dụng chúng trực tiếp.
anol

3
Có bởi vì mở rộng biến và hàm đượ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`pwd`được thực hiện bởi chính shell.
Chnossos

2
Không phải tất cả mọi người đều làm việc trên các tệp tin di sản, và thậm chí sau đó câu trả lời này là về việc biết khả năng này tồn tại.
Chnossos

1
Đây có thể là một tùy chọn gây phiền nhiễu / nguy hiểm vì chỉ có lệnh cuối cùng cho mục tiêu có thể gây ra lỗi (mọi lỗi thất bại trước đó sẽ bị bỏ qua) và có lẽ không ai làm việc với bạn sẽ mong đợi điều đó.
tobii

1
Đặt SHELLFLAGSthành -e -cvà shell sẽ thoát ở lệnh đầu tiên không thành công.
Timothy Baldwin

21

Đây là một mẹo dễ thương để đối phó với các thư mục và thực hiện. Thay vì sử dụng chuỗi multiline, hoặc "cd;" trên mỗi lệnh, xác định hàm chdir đơn giản như sau:

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ả những gì 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

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

1
Là thư mục hiện tại này sau đó được đặt cho các lệnh chỉ trong quy tắc đó hay cho tất cả các quy tắc được thực hiện sau đó? Ngoài ra, một số biến thể của công việc này trong Windows?
dùng117529

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

5
Đây là một hack xấu. Nếu bạn phải dùng đến những thứ như vậy, bạn không sử dụng Makefiles cho mục đích của chúng.
gatopeich

4
Tôi sẽ không đồng ý rằng đó là một hack xấu chắc chắn. Nhưng không chứng minh một số điều xấu xa bạn có thể làm.
JoeS

9

Bạn muốn nó làm gì khi nó đến đó? Mỗi lệnh được thực thi trong một lớp con, vì vậy lớp con thay đổi thư mục, nhưng kết quả cuối cùng là lệnh tiếp theo vẫn nằm trong thư mục hiện tại.

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

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

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

1

Để thay đổi thư mục

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir

-6

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!


1
Không, điều này là sai. Cụ thể, $(shell cd ....)được thực thi khi Makefile ban đầu được phân tích cú pháp, không phải khi công thức cụ thể này được chạy.
tripleee

@triplee Không hoàn toàn - chỉ $(shell)được mở rộng khi đưa ra quyết định xây dựng mục tiêu . Nếu thực hiện không bao giờ cần công thức, nó sẽ không mở rộng nó.
bobbogo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.