Câu trả lời ngắn: sử dụng"$@"
(lưu ý dấu ngoặc kép). Các hình thức khác rất hiếm khi hữu ích.
"$@"
là một cú pháp khá lạ. Nó được thay thế bởi tất cả các tham số vị trí, như các trường riêng biệt. Nếu không có tham số vị trí ( $#
là 0), thì "$@"
sẽ mở rộng thành không có gì (không phải là một chuỗi rỗng, mà là một danh sách có 0 phần tử), nếu có một tham số vị trí thì "$@"
tương đương với "$1"
, nếu có hai tham số vị trí thì "$@"
tương đương với "$1" "$2"
, Vân vân.
"$@"
cho phép bạn chuyển các đối số của tập lệnh hoặc hàm sang lệnh khác. Nó rất hữu ích cho các trình bao bọc thực hiện những việc như cài đặt biến môi trường, chuẩn bị tệp dữ liệu, v.v. trước khi gọi một lệnh có cùng các đối số và tùy chọn mà trình bao bọc được gọi.
Ví dụ, chức năng sau đây lọc đầu ra của cvs -nq update
. Ngoài bộ lọc đầu ra và trạng thái trả về (là trạng thái grep
thay vì của cvs
), việc gọi cvssm
một số đối số hoạt động giống như gọi cvs -nq update
với các đối số này.
cvssm () { cvs -nq update "$@" | egrep -v '^[?A]'; }
"$@"
mở rộng đến danh sách các tham số vị trí. Trong các shell hỗ trợ các mảng, có một cú pháp tương tự để mở rộng sang danh sách các phần tử của mảng: "${array[@]}"
(dấu ngoặc là bắt buộc ngoại trừ trong zsh). Một lần nữa, dấu ngoặc kép có phần sai lệch: chúng bảo vệ chống phân tách trường và tạo mẫu của các phần tử mảng, nhưng mỗi phần tử mảng kết thúc trong trường riêng của nó.
Một số shell cổ có lỗi được cho là: khi không có đối số vị trí, được "$@"
mở rộng thành một trường duy nhất chứa một chuỗi rỗng, thay vì không có trường. Điều này dẫn đến cách giải quyết${1+"$@"}
( nổi tiếng thông qua tài liệu Perl ). Chỉ các phiên bản cũ hơn của vỏ Bourne thực tế và việc triển khai OSF1 bị ảnh hưởng, không có sự thay thế tương thích hiện đại nào của nó (tro, ksh, bash, lỗi). /bin/sh
không bị ảnh hưởng trên bất kỳ hệ thống nào được phát hành trong thế kỷ 21 mà tôi biết (trừ khi bạn tính phát hành bảo trì Tru64 và thậm chí có /usr/xpg4/bin/sh
an toàn nên chỉ có #!/bin/sh
tập lệnh bị ảnh hưởng, không phải #!/usr/bin/env sh
tập lệnh miễn là PATH của bạn được thiết lập để tuân thủ POSIX) . Nói tóm lại, đây là một giai thoại lịch sử mà bạn không cần phải lo lắng.
"$*"
luôn mở rộng thành một từ. Từ này chứa các tham số vị trí, được nối với một khoảng trắng ở giữa. (Nói chung, dấu phân cách là ký tự đầu tiên của giá trị của IFS
biến. Nếu giá trị của IFS
là chuỗi rỗng, thì dấu phân cách là chuỗi rỗng.) Nếu không có tham số vị trí thì đó "$*"
là chuỗi trống, nếu có hai chuỗi trống tham số vị trí và IFS
có giá trị mặc định của nó sau đó "$*"
tương đương với "$1 $2"
, v.v.
$@
và $*
báo giá bên ngoài là tương đương. Chúng mở rộng sang danh sách các tham số vị trí, như các trường riêng biệt, như "$@"
; nhưng mỗi trường kết quả sau đó được chia thành các trường riêng biệt được coi là các mẫu ký tự đại diện tên tệp, như thường lệ với các mở rộng biến không được trích dẫn.
Ví dụ, nếu thư mục hiện hành chứa ba file bar
, baz
và foo
, sau đó:
set -- # no positional parameters
for x in "$@"; do echo "$x"; done # prints nothing
for x in "$*"; do echo "$x"; done # prints 1 empty line
for x in $*; do echo "$x"; done # prints nothing
set -- "b* c*" "qux"
echo "$@" # prints `b* c* qux`
echo "$*" # prints `b* c* qux`
echo $* # prints `bar baz c* qux`
for x in "$@"; do echo "$x"; done # prints 2 lines: `b* c*` and `qux`
for x in "$*"; do echo "$x"; done # prints 1 lines: `b* c* qux`
for x in $*; do echo "$x"; done # prints 4 lines: `bar`, `baz`, `c*` and `qux`