Truy cập dòng lệnh bash lập luận $ @ vs $ *


326

Trong nhiều câu hỏi SO và hướng dẫn bash tôi thấy rằng tôi có thể truy cập dòng lệnh args trong tập lệnh bash theo hai cách:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

Kết quả nào trong:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

Sự khác biệt giữa $*và là $@gì?
Khi nào nên sử dụng cái trước và khi nào nên sử dụng cái trước?


hãy xem câu trả lời này: stackoverflow.com/a/842325/671366
mã hóa

phân tích tĩnh trong IntelliJ coi echo "something $@"là một lỗi
Alex Cohn

Câu trả lời:


436

Sự khác biệt xuất hiện khi các tham số đặc biệt được trích dẫn. Hãy để tôi minh họa sự khác biệt:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

một ví dụ nữa về tầm quan trọng của việc trích dẫn: lưu ý có 2 khoảng trắng giữa "arg" và số, nhưng nếu tôi không trích dẫn $ word:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

và trong bash, "$@"là danh sách "mặc định" để lặp lại:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3

65
+1 Tôi luôn nghĩ rằng khái niệm này được thể hiện tốt nhất bằng một ví dụ đơn giản, trong đó hướng dẫn sử dụng bash hoàn toàn thiếu.
chepner

5
Có một trường hợp sử dụng có thể, khi $*hoặc "$*"có thể được yêu cầu, và mục đích không thể được phục vụ bởi $@hay "$@"?
anishsane

5
Phiên bản nào phù hợp hơn cho tập lệnh "trình bao bọc", trong đó các tham số tập lệnh cần trở thành tham số cho lệnh mới?
Segfault

7
@Segfault, trong trường hợp này, luôn luôn chọn "$@"với dấu ngoặc kép.
glenn jackman

2
Câu trả lời này chứa các ví dụ hữu ích nhưng sẽ tốt hơn nếu nó cũng giải thích cơ chế đằng sau chúng. Tại sao nó hoạt động như thế này?
Lii

254

Một bảng tổng quan tiện dụng đẹp từ Bash Hackers Wiki :

$ * so với $ @ bảng

nơi cở hàng thứ ba là ký tự đầu tiên của $IFS, lĩnh vực tách nội bộ, một biến vỏ.

Nếu các đối số sẽ được lưu trữ trong một biến tập lệnh và các đối số được dự kiến ​​sẽ chứa khoảng trắng, tôi hoàn toàn khuyên bạn nên sử dụng một "$*"mẹo với dấu tách trường bên trong $IFSđược đặt thành tab .


42
... Trong đó "c" là ký tự đầu tiên của $ IFS
glenn jackman

39
... và $IFSlà viết tắt của "Dấu tách trường nội bộ."
Serge Stroobandt

Dưới đây là một ví dụ , bao gồm đầu vào được trích dẫn. Đầu vào cũng có vấn đề!
Serge Stroobandt

Giả sử tôi muốn tạo một tập lệnh bao bọc không làm gì khác ngoài việc bắt chước chức năng của lệnh được gói. Tôi nên sử dụng cú pháp nào để chuyển các đối số từ tập lệnh bao bọc sang lệnh bên trong?
Marinos An

44

$ *

Mở rộng đến các tham số vị trí, bắt đầu từ một. Khi mở rộng xảy ra trong dấu ngoặc kép, nó mở rộng thành một từ duy nhất với giá trị của từng tham số được phân tách bằng ký tự đầu tiên của biến đặc biệt IFS. Nghĩa là, "$ *" tương đương với "$ 1c $ 2c ...", trong đó c là ký tự đầu tiên của giá trị của biến IFS. Nếu IFS không được đặt, các tham số được phân tách bằng dấu cách. Nếu IFS là null, các tham số được nối mà không có dấu phân cách.

$ @

Mở rộng đến các tham số vị trí, bắt đầu từ một. 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. Đó là, "$ @" tương đương với "$ 1" "$ 2" ... Nếu việc mở rộng dụng dấu ngoặc kép xảy ra trong vòng một từ, sự bành trướng của các tham số đầu tiên được nối với phần đầu của từ gốc, và việc mở rộng của tham số cuối cùng được nối với phần cuối của từ gốc. Khi không có tham số vị trí, "$ @" và $ @ mở rộng không có gì (ví dụ, họ bị loại bỏ).

Nguồn: Bash man


15

$ @ giống như $ *, nhưng mỗi tham số là một chuỗi được trích dẫn, nghĩa là các tham số được truyền nguyên vẹn, không có giải thích hoặc mở rộng. Điều này có nghĩa, trong số những thứ khác, mỗi tham số trong danh sách đối số được xem là một từ riêng biệt.

Tất nhiên, "$ @" nên được trích dẫn.

http://tldp.org/LDP/abs/html/iternalvariables.html#ARGLIST


1

Ví dụ này cho phép có thể làm nổi bật sự khác biệt giữa "at" và "asterix" trong khi chúng ta sử dụng chúng. Tôi tuyên bố hai mảng "trái cây" và "rau"

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "${fruits[*]}"            
printf "Fruits:\t%s\n" "${fruits[@]}"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "${vegetables[*]}"    
printf "Vegetables:\t%s\n" "${vegetables[@]}"    

Xem kết quả sau đây mã ở trên:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion

6
Nói một cách khoa học, cà chua là trái cây.
Randy

1
Bạn có quyền! "Trong thực vật học, một loại trái cây là cấu trúc hạt mang trong thực vật có hoa (hay còn gọi là thực vật hạt kín) hình thành từ các buồng trứng sau khi ra hoa." en.wikipedia.org/wiki/Fruit
Stefansson

@Randy: Nói một cách khoa học, tất cả các loại trái cây là rau (đó là một từ đồng nghĩa với "cây").
Cris Luengo

@CrisLuengo dị giáo! :)
Randy
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.