Truyền các đối số cho đến lượt thực hiện


354

Tôi sử dụng Makefiles.

Tôi có một mục tiêu được gọi là runchạy mục tiêu xây dựng. Đơn giản hóa, nó trông giống như sau:

prog: ....
  ...

run: prog
  ./prog

Có cách nào để vượt qua tranh luận? Vậy nên

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

Cảm ơn!


Câu trả lời:


264

Tôi không biết cách chính xác để làm những gì bạn muốn, nhưng cách giải quyết có thể là:

run: ./prog
    ./prog $(ARGS)

Sau đó:

make ARGS="asdf" run
# or
make run ARGS="asdf"

27
@Rob: $ () dễ mang theo hơn, nó hoạt động trong Nmake cũng như thực hiện.
John Knoeller

7
@Rob: Nmake chưa bao giờ hỗ trợ $ {} cho việc mở rộng macro và dường như đây là một hình thức cổ xưa được tạo ra. $ () được khuyến nghị bởi mọi hướng dẫn trực tuyến tôi đã xem. $ () cũng phù hợp hơn với các công cụ khác như bash.
John Knoeller

10
Có lẽ nó là cổ xưa. Tôi đã luôn sử dụng $ {}, nhưng hướng dẫn dành cho GNU Tạo trạng thái "Để thay thế giá trị của biến, hãy viết ký hiệu đô la theo sau tên của biến trong ngoặc đơn hoặc dấu ngoặc: $(foo)' or $ {foo} 'là tham chiếu hợp lệ cho biến 'foo'. " và tiến hành đưa ra các ví dụ trong đó chỉ sử dụng $ (). À
Jakob Borg

7
Chúc mừng John và bình tĩnh, tôi quay lại và thấy rằng lời đề nghị đến từ bản sao của cuốn sách OReilly phiên bản đầu tiên của tôi "Quản lý dự án với thực hiện". Tác giả nêu quy tắc về thay thế lưu trữ bằng cách sử dụng () và macro có thể thực hiện cả hai nhưng đề xuất sử dụng {} 'để phân biệt. Nhưng .... Phiên bản mới hiện có tên là "Quản lý dự án với GNU Make" sử dụng xuyên suốt (). Đi nào .... Đoán tôi sẽ phải hiện đại hóa! (-: Tôi vẫn còn ngạc nhiên rằng MS NMake barfs tại {} mặc dù
Rob Wells

1
@xealits Chắc chắn là vậy - có một ví dụ tại câu hỏi ở đây
helvete

198

Câu hỏi này đã gần ba tuổi, nhưng dù sao thì ...

Nếu bạn đang sử dụng GNU, điều này rất dễ thực hiện. Vấn đề duy nhất là makesẽ diễn giải các đối số không phải tùy chọn trong dòng lệnh là mục tiêu. Giải pháp là biến chúng thành mục tiêu không làm gì, vì vậy makesẽ không phàn nàn:

# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "run"
  RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(RUN_ARGS):;@:)
endif

prog: # ...
    # ...

.PHONY: run
run : prog
    @echo prog $(RUN_ARGS)

Chạy này cho:

$ make run foo bar baz
prog foo bar baz

2
Điều này thật tuyệt vời ngoại trừ nó dường như không hoạt động đối với các đối số bắt đầu bằng dấu gạch ngang:prog foo bar --baz
ingydotnet

22
Nó cũng hoạt động trong trường hợp đó, nhưng bạn phải nói makekhông diễn giải --baznhư một tùy chọn dòng lệnh : make -- prog foo bar --baz. Có --nghĩa là "mọi thứ sau đây là một đối số, không phải là một tùy chọn".
Idelic

Làm thế nào tôi có thể xác định một giá trị mặc định để RUN_ARGSsử dụng này?
Bouke

Có thể thêm một elsenhánh vào ifeqvà đặt RUN_ARGSở đó?
Idelic

1
Điểm tốt màu xanh! Nhưng có một giải pháp cho điều đó: thay thế dòng 'eval' bằng cách $(eval $(RUN_ARGS):dummy;@:)không xác định mục tiêu giả.
Lucas Cimon

52

đối với tiêu chuẩn, bạn có thể truyền các đối số bằng cách xác định các macro như thế này

make run arg1=asdf

sau đó sử dụng chúng như thế này

run: ./prog $(arg1)
   etc

Tài liệu tham khảo về sản phẩm NMake của Microsoft


34

Bạn có thể truyền biến cho Makefile như bên dưới:

run:
    @echo ./prog $$FOO

Sử dụng:

$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat

hoặc là:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat

Hoặc sử dụng giải pháp do Beta cung cấp :

run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:

%:- quy tắc phù hợp với bất kỳ tên nhiệm vụ; @:- công thức trống = không làm gì cả

Sử dụng:

$ make run the dog kicked the cat
./prog the dog kicked the cat

22

TL; DR đừng cố làm điều này

$ make run arg

thay vì tạo tập lệnh:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

và làm điều này:

$ ./buildandrunprog.sh arg

trả lời cho câu hỏi đã nêu:

bạn có thể sử dụng một biến trong công thức

run: prog
    ./prog $(var)

sau đó chuyển một phép gán biến làm đối số để thực hiện

$ make run var=arg

Điều này sẽ thực thi ./prog arg.

nhưng hãy cẩn thận với những cạm bẫy. tôi sẽ giải thích chi tiết về những cạm bẫy của phương pháp này và các phương pháp khác.


trả lời cho ý định giả định đằng sau câu hỏi:

giả định: bạn muốn chạy progvới một số đối số nhưng phải xây dựng lại trước khi chạy nếu cần thiết.

Câu trả lời: tạo một kịch bản xây dựng lại nếu cần thiết sau đó chạy prog với args

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

kịch bản này làm cho ý định rất rõ ràng. nó sử dụng make để làm những gì nó tốt cho: xây dựng. nó sử dụng tập lệnh shell để làm những gì tốt cho: xử lý hàng loạt.

cộng với bạn có thể làm bất cứ điều gì khác mà bạn có thể cần với sự linh hoạt và biểu cảm hoàn toàn của một tập lệnh shell mà không cần tất cả sự cẩn thận của một tập tin.

Ngoài ra cú pháp gọi bây giờ giống hệt như thực tế:

$ ./buildandrunprog.sh foo "bar baz"

so với:

$ ./prog foo "bar baz"

tương phản với

$ make run var="foo bar\ baz"

lý lịch:

make không được thiết kế để truyền đối số cho mục tiêu. tất cả các đối số trên dòng lệnh được hiểu là mục tiêu (còn gọi là mục tiêu), dưới dạng tùy chọn hoặc dưới dạng gán.

Vì vậy, nếu bạn chạy này:

$ make run foo --wat var=arg

thực hiện sẽ giải thích runfoonhư là mục tiêu (mục tiêu) để cập nhật theo công thức nấu ăn của họ. --watnhư là một lựa chọn để thực hiện. và var=argnhư một sự phân công biến.

để biết thêm chi tiết, xem: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

để biết thuật ngữ, hãy xem: https://www.gnu.org/software/make/manual/html_node/Rule-Intributiontion.html#Rule-Int sinhtion


về phương pháp gán biến và tại sao tôi khuyên bạn nên chống lại nó

$ make run var=arg

và biến trong công thức

run: prog
    ./prog $(var)

đây là cách "chính xác" và đơn giản nhất để truyền các đối số vào một công thức. nhưng trong khi nó có thể được sử dụng để chạy một chương trình với các đối số thì chắc chắn nó không được thiết kế để sử dụng theo cách đó. xem https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

theo tôi điều này có một nhược điểm lớn: những gì bạn muốn làm là chạy progtheo lập luận arg. nhưng thay vì viết:

$ ./prog arg

bạn đang viết:

$ make run var=arg

điều này càng trở nên khó xử hơn khi cố gắng vượt qua nhiều đối số hoặc đối số có chứa khoảng trắng:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

so với:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz

Đối với hồ sơ, đây là những gì tôi progtrông giống như:

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done

cũng lưu ý rằng bạn không nên đặt $(var)dấu ngoặc kép trong tệp thực hiện:

run: prog
    ./prog "$(var)"

bởi vì sau đó progsẽ luôn chỉ nhận được một đối số:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz

tất cả điều này là lý do tại sao tôi đề nghị chống lại tuyến đường này.


để hoàn thiện, đây là một số phương pháp khác để "truyền đối số để chạy".

phương pháp 1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

giải thích siêu ngắn: lọc ra mục tiêu hiện tại từ danh sách các mục tiêu. tạo bắt tất cả mục tiêu ( %) mà không có gì để âm thầm bỏ qua các mục tiêu khác.

phương pháp 2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

giải thích siêu ngắn: nếu mục tiêu là runsau đó loại bỏ mục tiêu đầu tiên và tạo ra mục tiêu không làm gì cho các mục tiêu còn lại sử dụng eval.

cả hai sẽ cho phép bạn viết một cái gì đó như thế này

$ make run arg1 arg2

để giải thích sâu hơn, hãy nghiên cứu hướng dẫn sử dụng: https://www.gnu.org/software/make/manual/html_node/index.html

vấn đề của phương pháp 1:

  • các đối số bắt đầu bằng dấu gạch ngang sẽ được diễn giải bằng cách thực hiện và không được thông qua dưới dạng mục tiêu.

    $ make run --foo --bar
    

    cách giải quyết

    $ make run -- --foo --bar
    
  • các đối số có dấu bằng sẽ được giải thích bằng cách thực hiện và không được thông qua

    $ make run foo=bar
    

    không có cách giải quyết

  • tranh luận với không gian là khó xử

    $ make run foo "bar\ baz"
    

    không có cách giải quyết

  • nếu một đối số xảy ra là run(bằng với mục tiêu) thì nó cũng sẽ bị xóa

    $ make run foo bar run
    

    sẽ chạy ./prog foo barthay vì./prog foo bar run

    giải pháp có thể với phương pháp 2

  • nếu một đối số là mục tiêu hợp pháp thì nó cũng sẽ được chạy.

    $ make run foo bar clean
    

    sẽ chạy ./prog foo bar cleannhưng cũng là công thức cho mục tiêu clean(giả sử nó tồn tại).

    giải pháp có thể với phương pháp 2

  • Khi bạn gõ nhầm một mục tiêu hợp pháp, nó sẽ bị bỏ qua một cách âm thầm vì bắt tất cả mục tiêu.

    $ make celan
    

    Sẽ chỉ âm thầm làm ngơ celan.

    cách giải quyết là làm cho mọi thứ dài dòng. để bạn thấy những gì xảy ra nhưng điều đó tạo ra rất nhiều tiếng ồn cho đầu ra hợp pháp.

vấn đề của phương pháp 2:

  • nếu một đối số có cùng tên với một mục tiêu hiện có thì thực hiện sẽ in một cảnh báo rằng nó đang bị ghi đè.

    không có cách giải quyết mà tôi biết

  • các đối số có dấu bằng sẽ vẫn được giải thích bằng cách thực hiện và không được thông qua

    không có cách giải quyết

  • tranh luận với không gian vẫn còn lúng túng

    không có cách giải quyết

  • đối số với phá vỡ không gian evalcố gắng tạo ra không làm gì mục tiêu.

    cách giải quyết: tạo ra toàn cầu bắt tất cả các mục tiêu không làm gì như trên. với vấn đề như trên, nó sẽ lại âm thầm bỏ qua các mục tiêu hợp pháp bị đánh lừa.

  • nó sử dụng evalđể sửa đổi tệp thực hiện trong thời gian chạy. bạn có thể tệ hơn bao nhiêu về khả năng đọc và gỡ lỗi và Nguyên tắc ít ngạc nhiên nhất .

    cách giải quyết: không làm điều này !! 1 thay vào đó hãy viết một tập lệnh shell chạy và sau đó chạy prog.

tôi chỉ thử nghiệm bằng cách sử dụng gnu. làm cho khác có thể có hành vi khác nhau.


TL; DR đừng cố làm điều này

$ make run arg

thay vì tạo tập lệnh:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

và làm điều này:

$ ./buildandrunprog.sh arg

12

Đây là một giải pháp khác có thể giúp với một số trường hợp sử dụng sau:

test-%:
    $(PYTHON) run-tests.py $@

Nói cách khác, chọn một số tiền tố ( test-trong trường hợp này), sau đó chuyển trực tiếp tên đích đến chương trình / người chạy. Tôi đoán điều này chủ yếu hữu ích nếu có một số kịch bản chạy liên quan có thể mở khóa tên đích thành một cái gì đó hữu ích cho chương trình cơ bản.


2
Bạn cũng có thể sử dụng $*để vượt qua chỉ một phần của mục tiêu phù hợp với %.
Malvineous

9

Không. Nhìn vào cú pháp từ trang man cho GNU

thực hiện [-f makefile] [tùy chọn] ... [mục tiêu] ...

bạn có thể chỉ định nhiều mục tiêu, do đó 'không' (ít nhất là không theo cách chính xác mà bạn đã chỉ định).


4

Bạn có thể trích xuất rõ ràng từng đối số thứ n trong dòng lệnh. Để làm điều này, bạn có thể sử dụng biến MAKECMDGOALS, nó giữ danh sách các đối số dòng lệnh được đưa ra để 'make', mà nó diễn giải như một danh sách các mục tiêu. Nếu bạn muốn trích xuất đối số thứ n, bạn có thể sử dụng biến đó kết hợp với hàm "word", ví dụ, nếu bạn muốn đối số thứ hai, bạn có thể lưu trữ nó trong một biến như sau:

second_argument := $(word 2, $(MAKECMDGOALS) )

Điều này cũng chạy lệnh make cho đối số đó. make: *** No rule to make target 'arg'. Stop.
ThomasReggi

2

Anon , run: ./progtrông hơi lạ, vì phần bên phải là mục tiêu, vì vậyrun: prog vẻ tốt hơn.

Tôi muốn đề nghị một cách đơn giản:

.PHONY: run

run:
        prog $(arg1)

và tôi muốn thêm, các đối số có thể được thông qua:

  1. như là đối số: make arg1="asdf" run
  2. hoặc được định nghĩa là môi trường: arg1="asdf" make run

2

Đây là ví dụ của tôi. Lưu ý rằng tôi đang viết trong Windows 7, sử dụng mingw32-make.exe đi kèm với Dev-Cpp. (Tôi có c: \ Windows \ System32 \ make.bat, vì vậy lệnh vẫn được gọi là "make".)

clean:
    $(RM) $(OBJ) $(BIN) 
    @echo off
    if "${backup}" NEQ "" ( mkdir ${backup} 2> nul && copy * ${backup} )

Cách sử dụng để vệ sinh thường xuyên:

make clean

Sử dụng để làm sạch và tạo bản sao lưu trong mydir /:

make clean backup=mydir

2

Không quá tự hào về điều này, nhưng tôi không muốn vượt qua các biến môi trường nên tôi đã đảo ngược cách chạy lệnh đóng hộp:

run:
    @echo command-you-want

điều này sẽ in lệnh bạn muốn chạy, vì vậy chỉ cần đánh giá nó trong một khung con:

$(make run) args to my command

4
nhìn lại câu trả lời này hai năm sau - tại sao tôi lại bướng bỉnh đến mức tôi không muốn sử dụng các biến môi trường và tại sao tôi lại nghĩ rằng việc tạo ra một lệnh khác là tốt hơn?
Conrad.Dean

0

Tôi đã tìm thấy một cách để có được các đối số có dấu bằng (=)! Câu trả lời đặc biệt là một bổ sung cho câu trả lời của @lesmana (vì nó là câu trả lời đầy đủ nhất và được giải thích ở đây), nhưng nó sẽ quá lớn để viết nó như một bình luận. Một lần nữa, tôi nhắc lại thông điệp của anh ấy: TL; DR đừng cố làm điều này!

Tôi cần một cách để xử lý đối số của mình --xyz-enabled=false(vì mặc định là đúng), điều mà tất cả chúng ta đều biết rằng đây không phải là mục tiêu thực hiện và do đó không phải là một phần của$(MAKECMDGOALS) .

Trong khi xem xét tất cả các biến tạo bằng cách lặp lại, $(.VARIABLES)tôi nhận được các kết quả thú vị sau:

[...] -*-command-variables-*- --xyz-enabled [...]

Điều này cho phép chúng tôi đi theo hai cách: hoặc là nhận được tất cả bắt đầu với một --(nếu áp dụng cho trường hợp của bạn), hoặc nhìn vào GNU làm cụ thể (có lẽ không dành cho chúng tôi để sử dụng) biến -*-command-variables-*-. ** Xem chân trang để biết các tùy chọn bổ sung ** Trong trường hợp của tôi, biến này được giữ:

--xyz-enabled=false

Với biến này, chúng ta có thể kết hợp nó với giải pháp đã có sẵn $(MAKECMDGOALS)và do đó bằng cách xác định:

# the other technique to invalidate other targets is still required, see linked post
run:
    @echo ./prog $(-*-command-variables-*-) $(filter-out $@,$(MAKECMDGOALS))`

và sử dụng nó với (trộn rõ thứ tự các đối số):

make run -- config --xyz-enabled=false over=9000 --foo=bar show  isit=alwaysreversed? --help

trả lại:

./prog isit=alwaysreversed? --foo=bar over=9000 --xyz-enabled=false config show --help

Như bạn có thể thấy, chúng tôi mất tổng số thứ tự của các đối số. Phần có phần "gán" dường như đã bị đảo ngược, thứ tự của phần "mục tiêu" được giữ lại. Tôi đã đặt "nhiệm vụ" ngay từ đầu, hy vọng chương trình của bạn không quan tâm đến nơi tranh luận được đặt.


Cập nhật: sau đây làm cho các biến có vẻ hứa hẹn là tốt:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false

-2

Một mẹo khác tôi sử dụng là -ncờ, makeđể thực hiện chạy khô. Ví dụ,

$ make install -n 
# Outputs the string: helm install stable/airflow --name airflow -f values.yaml
$ eval $(make install -n) --dry-run --debug
# Runs: helm install stable/airflow --name airflow -f values.yaml --dry-run --debug
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.