Tuyên truyền tất cả các đối số trong một tập lệnh bash shell


852

Tôi đang viết một tập lệnh rất đơn giản gọi một tập lệnh khác và tôi cần truyền các tham số từ tập lệnh hiện tại của mình sang tập lệnh mà tôi đang thực thi.

Ví dụ, tên tập lệnh của tôi là foo.shvà các cuộc gọibar.sh

foo.sh:

bar $1 $2 $3 $4

Làm thế nào tôi có thể làm điều này mà không chỉ định rõ ràng từng tham số?



Câu trả lời:


1407

Sử dụng "$@"thay vì đơn giản $@nếu bạn thực sự muốn các tham số của bạn được thông qua như nhau.

Quan sát:

$ cat foo.sh
#!/bin/bash
baz.sh $@

$ cat bar.sh
#!/bin/bash
baz.sh "$@"

$ cat baz.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./foo.sh first second
Received: first
Received: second
Received:
Received:

$ ./foo.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./bar.sh first second
Received: first
Received: second
Received:
Received:

$ ./bar.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:

7
Điều này thực hiện công việc này để vượt qua các chuỗi được trích dẫn / thoát: Quan sát: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping thư mục thanh tôi Có thể có tập lệnh shell có thể thấy dòng lệnh gốc thực sự hoàn thành với dấu ngoặc kép hoặc chuỗi thoát?
Đánh dấu Edington

3
Còn việc chuyền cờ thì sao? Ví dụ: './bar.sh --with-Stuff'
Nathan Long

4
Tôi đề nghị bất cứ ai muốn hiểu rõ hơn về chủ đề Chia tách từ, hãy đọc thêm về nó ở đây.
Rany Albeg Wein

4
Tôi đề nghị bạn chỉ ra những gì xảy ra khi bao gồm cả 'arg with spaces'ba ví dụ. Tôi đã ngạc nhiên với kết quả; Hy vọng bạn có thể giải thích chúng.
Michael Scheper

2
@MichaelScheper, dù sao đi nữa, ./foo.sh "arg with spaces"./foo.sh 'arg with spaces'giống hệt nhau 100%, vì vậy tôi không thấy đề xuất thêm nó vào các ví dụ được đưa ra sẽ giúp ích như thế nào.
Charles Duffy

475

Đối với bash và các vỏ giống như Bourne khác:

java com.myserver.Program "$@"

13
@Amir: Không, không phải cho csh . Vì sự tỉnh táo của mọi người: đừng viết kịch bản csh . Nhưng tôi nghĩ có thể $argv:qsẽ làm việc trong một số biến thể csh.
Chris Johnsen

2
Cảm ơn! Như mong muốn, điều này vượt qua cùng một tập hợp các đối số mà kịch bản nhận được - không phải là một đối số lớn. Các trích dẫn kép là cần thiết. Hoạt động ngay cả khi với các đối số được trích dẫn bao gồm khoảng trắng.
Andy Thomas

24
Liên quan: nếu tập lệnh shell của bạn chỉ đóng vai trò là trình bao bọc để chạy java, hãy xem xét việc tạo dòng cuối cùng exec java com.myserver.Program "$@"Điều này khiến bash thực thi thành java, thay vì chờ đợi nó hoàn thành. Vì vậy, bạn đang sử dụng một khe quá trình ít hơn. Ngoài ra, nếu quy trình cha mẹ (chạy tập lệnh của bạn) đang xem nó thông qua pid và hy vọng nó là quy trình 'java', một số điều bất thường có thể bị phá vỡ nếu bạn không thực hiện; exec thực hiện java để kế thừa pid tương tự.
greggo

7
@dragonxlwang: Bạn có thể sử dụng một biến mảng, nếu vỏ hỗ trợ của bạn họ (ví dụ bash , zsh , những người khác, nhưng không đơn giản Bourne- hoặc POSIX vỏ): tiết kiệm để args với args=("$@")và mở rộng mỗi phần tử là một vỏ “từ” riêng biệt ( giống như "$@") với "${args[@]}".
Chris Johnsen

1
Có bất kỳ "gotchas" nào cần lưu ý khi sử dụng "$@", như nó sẽ thất bại nếu bạn đã thoát khoảng trắng trong một đối số, hoặc ký tự null hoặc các ký tự đặc biệt khác?
IQAndreas

97

Sử dụng "$@"(hoạt động cho tất cả các compatibles POSIX ).

[...], bash có biến "$ @", mở rộng ra tất cả các tham số dòng lệnh được phân tách bằng dấu cách.

Từ Bash bằng ví dụ .


6
Vì vậy, "$ @" không chỉ là dấu ngoặc kép quanh $ @ mà thực chất là một biến được xây dựng khác nhau?
ben

5
@ben Đó là một biến duy nhất nhưng nó yêu cầu các dấu ngoặc kép xung quanh nó để có một giá trị hữu ích khác biệt với (bị hỏng) $*. Tôi tin rằng có sự tiến bộ lịch sử ở đây; $*đã không làm việc như thiết kế, vì vậy $@đã được phát minh để thay thế nó; nhưng các quy tắc trích dẫn là những gì chúng là, các trích dẫn kép xung quanh nó vẫn được yêu cầu (hoặc nó sẽ trở lại $*ngữ nghĩa bị hỏng ).
tripleee

7
"Vì vậy," $ @ "không chỉ là dấu ngoặc kép quanh $ @ mà thực chất là một biến được xây dựng khác nhau?" - Đối với tất cả ý định và mục đích, có: stackoverflow.com/a/28099707/162094
Stuart Berg

1
Nếu bạn gọi một tập lệnh chứa echo "$@"như ./script.sh a "b c" dsau đó bạn chỉ nhận được a b c dthay vì a "b c" d, đó là rất khác nhau.
isarandi

7
@isarandi Mặc dù đúng là đầu ra không còn chứa dấu ngoặc kép, đó là những gì bạn mong đợi ... nhưng hãy yên tâm rằng trong tập lệnh, echosẽ nhận được ba đối số: "a" "b c" "d"(sau đó shell kết hợp chúng với nhau như là một phần của việc mở rộng chuỗi của nó). Nhưng nếu bạn đã sử dụng for i in "$@"; do echo $i; doneBạn sẽ nhận được a⏎b c⏎d.
FeRD

64

Tôi nhận ra điều này đã được trả lời tốt nhưng đây là so sánh giữa "$ @" $ @ "$ *" và $ *

Nội dung của kịch bản thử nghiệm:

# cat ./test.sh
#!/usr/bin/env bash
echo "================================="

echo "Quoted DOLLAR-AT"
for ARG in "$@"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-AT"
for ARG in $@; do
    echo $ARG
done

echo "================================="

echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
    echo $ARG
done

echo "================================="

Bây giờ, hãy chạy tập lệnh thử nghiệm với các đối số khác nhau:

# ./test.sh  "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================

1
trình diễn ngắn gọn và đóng góp cho câu trả lời này.
Merlin

33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Chỉ cần nghĩ rằng điều này có thể hữu ích hơn một chút khi cố gắng kiểm tra xem args đi vào kịch bản của bạn như thế nào


6
Điều này chắc chắn không giúp trả lời câu hỏi của anh ấy nhưng điều này thực sự hữu ích. nâng cao tinh thần!
Dario Russo

2
Nó phá vỡ khi một tham số trống được thông qua. Bạn nên kiểm tra$#
Sebi

người kiểm tra lý lẽ tốt, đồng ý "", ''như một cuộc tranh luận, nếu không có tranh luận thì đó là sự im lặng. Tôi đã cố gắng khắc phục điều này, nhưng cần một vòng lặp for và counter with $#. Tôi vừa thêm cái này vào cuối:echo "End of args or received quoted null"
Merlin

8

SUN Unix của tôi có rất nhiều hạn chế, thậm chí "$ @" không được hiểu như mong muốn. Cách giải quyết của tôi là $ {@}. Ví dụ,

#!/bin/ksh
find ./ -type f | xargs grep "${@}"

Nhân tiện, tôi phải có tập lệnh đặc biệt này vì Unix của tôi cũng không hỗ trợ grep -r


Đây là một câu hỏi Bash; bạn đang sử dụngksh
tripleee

6

Nếu bạn bao gồm $@trong một chuỗi được trích dẫn với các ký tự khác thì hành vi rất kỳ quặc khi có nhiều đối số, chỉ có đối số đầu tiên được đưa vào bên trong dấu ngoặc kép.

Thí dụ:

#!/bin/bash
set -x
bash -c "true foo $@"

Sản lượng:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz

Nhưng việc gán cho một biến khác trước:

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"

Sản lượng:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'

3
Tôi sẽ không phủ nhận điều đó gây bối rối, nhưng nó thực sự có ý nghĩa trong ngữ nghĩa của "$@"bash. Nó cũng giúp minh họa sự khác biệt chính giữa $@$*, và tại sao cả hai đều hữu ích. Từ bash(1)trang người đàn ông đặc biệt thông số phần: " *- Khi mở rộng xảy ra trong vòng ngoặc kép, nó mở rộng đến một từ duy nhất với giá trị của mỗi tham số [...] Đó là, "$*"tương đương với "$1c$2c...", nơi clà [ $IFS]." Và thực tế, sử dụng $*thay vì $@trong ví dụ đầu tiên của bạn sẽ có kết quả đầu ra giống hệt với phiên bản thứ hai.
FeRD

2
Bây giờ, so sánh với "$@". Một lần nữa từ trang man: " @- Khi mở rộng xảy ra trong dấu ngoặc kép, mỗi tham số sẽ mở rộng thành một từ riêng biệt. Điều đó "$@"tương đương với "$1" "$2"Thẻ Nếu việc mở rộng trích dẫn kép xảy ra trong một từ, việc mở rộng tham số đầu tiên được nối với phần đầu của từ gốc và việc mở rộng tham số cuối cùng được nối với phần cuối của từ gốc. " ... Và thực sự, nếu mã của bạn đã được bash -c "true foo $@ bar baz", thì hãy chạy nó như test.sh one twomạng bash -c 'true foo one' 'two bar baz'.
FeRD

1
Cảm ơn các tài liệu trích dẫn và thông tin về $*, tôi dường như quên rằng nó tồn tại ..
ColinM

Heh. Tôi thì ngược lại, $@chỉ đạt được lực kéo khi lần đầu tiên bắt đầu viết kịch bản shell, tôi vẫn phải tự nhắc nhở mình rằng nó ở đó. Người ta thường thấy "$*"được sử dụng trong các kịch bản ... sau đó tác giả sẽ nhận ra rằng nó đã phá vỡ tất cả các đối số của họ với nhau, vì vậy họ sẽ thử mọi cách vô nghĩa phức tạp với tách từ "$*"hoặc [ ghép lại] một danh sách đối số bằng cách lặp qua shiftđể kéo chúng xuống từng cái một ... chỉ cần sử dụng để $@giải quyết nó. (Giúp bash sử dụng cùng một kiểu ghi nhớ để truy cập các thành viên mảng: ${var[*]}đối với tất cả chúng là một từ, ${var[@]}cho một danh sách các từ.)
FeRD

Vấn đề thực sự ở đây là bạn đang sử dụng bash -ctheo cách hoàn toàn vô nghĩa.
tripleee

4

Đôi khi bạn muốn vượt qua tất cả các đối số của mình, nhưng trước một cờ (ví dụ --flag)

$ bar --flag "$1" --flag "$2" --flag "$3"

Bạn có thể làm điều này theo cách sau:

$ bar $(printf -- ' --flag "%s"' "$@")

Lưu ý: để tránh thêm tách lĩnh vực, bạn phải trích dẫn %s$@, và để tránh việc một chuỗi duy nhất, bạn không thể trích dẫn subshell của printf.


3

Hoạt động tốt, ngoại trừ nếu bạn có không gian hoặc ký tự thoát. Tôi không tìm cách nắm bắt các đối số trong trường hợp này và gửi đến một ssh bên trong tập lệnh.

Điều này có thể hữu ích nhưng rất xấu

_command_opts=$( echo "$@" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&@",$i) ; gsub(/ $/,"",$i );gsub (/$/,"@",$i) }; print $0 }' | tr '@' \' )

3

Rất nhiều câu trả lời ở đây khuyến nghị $@hoặc $*có và không có trích dẫn, tuy nhiên dường như không ai giải thích được những gì thực sự làm và tại sao bạn nên làm theo cách đó. Vì vậy, hãy để tôi ăn cắp bản tóm tắt tuyệt vời này từ câu trả lời này :

nhập mô tả hình ảnh ở đây

Lưu ý rằng các trích dẫn làm cho tất cả sự khác biệt và không có cả hai đều có hành vi giống hệt nhau.

Với mục đích của tôi, tôi cần chuyển các tham số từ tập lệnh này sang tập lệnh khác và tùy chọn tốt nhất là:

# file: parent.sh
# we have some params passed to parent.sh 
# which we will like to pass on to child.sh as-is

./child.sh $*

Lưu ý không có trích dẫn và $@nên làm việc tốt trong tình huống trên.


2

bar "$@" sẽ tương đương với bar "$1" "$2" "$3" "$4"

Lưu ý rằng các dấu ngoặc kép là quan trọng!

"$@", $@, "$*"Hoặc$* sẽ từng cư xử hơi khác nhau liên quan đến thoát và nối như mô tả trong này câu trả lời stackoverflow .

Một trường hợp sử dụng liên quan chặt chẽ là chuyển tất cả các đối số đã cho bên trong một đối số như thế này:

bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".

Tôi sử dụng một biến thể của câu trả lời của @ kaugeour để đạt được điều này:

bash -c "bar $(printf -- '"%s" ' "$@")"

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.