2012-04-05 3 views
56

Tôi đang cố gắng sử dụng các kết quả của ls trong lệnh khác (ví dụ như tiếng vang, rsync):Làm thế nào để sử dụng các lệnh shell trong Makefile

Nhưng tôi nhận được:

make 
FILES = Makefile file1.tgz file2.tgz file3.tgz 
make: FILES: No such file or directory 
make: *** [all] Error 1 

tôi đã thử sử dụng echo $$FILES, echo ${FILES}echo $(FILES), không có may mắn.

Trả lời

79

Với:

FILES = $(shell ls) 

thụt vào bên dưới all như vậy, đó là một lệnh xây dựng. Vì vậy, điều này mở rộng $(shell ls), sau đó cố gắng chạy lệnh FILES ....

Nếu FILES được coi là một biến make, các biến này cần phải được chỉ định bên ngoài phần công thức, ví dụ:

FILES = $(shell ls) 
all: 
     echo $(FILES) 

Tất nhiên, điều đó có nghĩa là FILES sẽ được thiết lập để "đầu ra từ ls" trước khi chạy bất kỳ lệnh nào tạo tệp .tgz. (Mặc dù như Kaz notes biến được tái mở rộng mỗi lần, vì vậy cuối cùng nó sẽ bao gồm các tập tin .tgz, một số làm cho các biến thể có FILES := ... để tránh điều này, cho hiệu quả và/hoặc đúng đắn .)

Nếu FILES là coi là một biến vỏ, bạn có thể đặt nó nhưng bạn cần phải làm điều đó trong vỏ ese, không có dấu cách, và trích dẫn:

all: 
     FILES="$(shell ls)" 

Tuy nhiên, mỗi dòng được điều hành bởi một lớp vỏ riêng biệt, vì vậy biến này sẽ không tồn tại ở dòng tiếp theo, vì vậy, bạn phải sử dụng ngay lập tức:

 FILES="$(shell ls)"; echo $$FILES 

Đây là tất cả một chút ngớ ngẩn kể từ khi vỏ sẽ mở rộng * (và khác biểu vỏ glob) cho bạn ở nơi đầu tiên, vì vậy bạn có thể chỉ:

 echo * 

như lệnh shell của bạn.

Cuối cùng, như một quy luật chung (không thực sự áp dụng đối với ví dụ này): như esperanto ghi chú trong các ý kiến, bằng cách sử dụng đầu ra từ ls là không hoàn toàn đáng tin cậy (một số chi tiết phụ thuộc vào tên tập tin và đôi khi ngay cả những phiên bản của ls; một số phiên bản ls cố gắng khử trùng đầu ra trong một số trường hợp). Vì vậy, như là l0b0idelic lưu ý, nếu bạn đang sử dụng GNU, bạn có thể sử dụng $(wildcard)$(subst ...) để tự hoàn thành mọi thứ bên trong make (tránh bất kỳ vấn đề lạ nào trong tên tệp). (Trong sh kịch bản, trong đó có phần công thức của makefiles, phương pháp khác là sử dụng find ... -print0 | xargs -0 để tránh vấp ngã trên khoảng trống, dòng mới, ký tự điều khiển, và vân vân.)


The GNU Make documentation notes further that POSIX make added ::= assignment in 2012.Tôi đã không tìm thấy một liên kết tham khảo nhanh đến một tài liệu POSIX cho điều này, tôi cũng không biết off-hand mà make biến thể hỗ trợ ::= chuyển nhượng, mặc dù GNU làm làm ngày hôm nay, với ý nghĩa tương tự như :=, ví dụ, làm nhiệm vụ ngay bây giờ với sự bành trướng.

Lưu ý rằng VAR := $(shell command args...) cũng có thể được viết là VAR != command args... trong một số biến thể make, bao gồm tất cả biến thể GNU và BSD hiện đại theo như tôi biết. Những biến thể khác không có $(shell) nên sử dụng VAR != command args... là vượt trội trong cả hai là ngắn hơn làm việc trong các biến thể hơn.

+0

Cảm ơn. Tôi muốn sử dụng một lệnh phức tạp ('ls' với' sed' và cắt, ví dụ) và sau đó sử dụng kết quả trong rsync và các lệnh khác. Tôi có phải lặp lại câu lệnh dài dòng không? Tôi không thể lưu trữ các kết quả trong một biến Make nội bộ? –

+1

Gnu làm có thể có một cách để làm điều đó, nhưng tôi chưa bao giờ sử dụng nó, và tất cả các makefiles khủng khiếp phức tạp, chúng tôi sử dụng chỉ cần sử dụng các biến shell và các lệnh shell one-liner khổng lồ được xây dựng với "; \" vào cuối mỗi dòng khi cần thiết. (Không thể có được mã hóa mã để làm việc với các chuỗi dấu chéo ngược ở đây, hmm) – torek

+0

Ngoài ra, [thay vì 'ls'] (http://mywiki.wooledge.org/ParsingLs), bạn sẽ muốn sử dụng [' wildcard' make builtin] (https://www.gnu.org/software/make/manual/make.html#Wildcard-Function). – l0b0

29

Ngoài ra, ngoài việc torek của câu trả lời: có một điều mà đứng ra là bạn đang sử dụng một giao vĩ mô một cách lười biếng-đánh giá.

Nếu bạn đang sử dụng GNU Make, hãy sử dụng bài tập := thay vì =. Nhiệm vụ này làm cho phía bên phải được mở rộng ngay lập tức và được lưu trữ trong biến bên tay trái.

FILES := $(shell ...) # expand now; FILES is now the result of $(shell ...) 

FILES = $(shell ...) # expand later: FILES holds the syntax $(shell ...) 

Nếu bạn sử dụng phân =, nó có nghĩa là mỗi lần xuất hiện của $(FILES) sẽ được mở rộng cú pháp $(shell ...) và do đó gọi lệnh shell. Điều này sẽ làm cho công việc của bạn chạy chậm hơn, hoặc thậm chí có một số hậu quả đáng ngạc nhiên.

+0

Bây giờ chúng ta có danh sách làm thế nào để chúng ta lặp qua từng mục trong danh sách và thực hiện một lệnh trên nó? Chẳng hạn như xây dựng hoặc thử nghiệm? – anon58192932

+0

@ anon58192932 Sự lặp lại cụ thể đó, để thực hiện một lệnh, thường được thực hiện trong một đoạn cú pháp vỏ trong một công thức xây dựng, vì vậy nó xuất hiện trong trình bao, chứ không phải trong thực hiện: 'for x in $ (FILES); làm lệnh $$ x; done'. Lưu ý rằng tăng gấp đôi lên '$$' mà chuyển một '$' duy nhất cho trình bao. Ngoài ra, các mảnh vỏ là một lớp lót; để viết mã shell nhiều dòng, bạn sử dụng liên tục dấu gạch chéo ngược được xử lý bởi chính 'make' và được xếp thành một dòng. Điều này có nghĩa là dấu chấm phẩy phân tách bằng lệnh là bắt buộc. – Kaz