Cấu trúc dữ liệu của $ @ in shell là gì?


13

Chúng tôi thường sử dụng $@để đại diện cho tất cả các đối số ngoại trừ $ 0. Tuy nhiên, tôi không biết cấu trúc dữ liệu $@là gì.

Tại sao nó hành xử khác với $*khi bao gồm trong trích dẫn kép, bất cứ ai cũng có thể cho tôi một lời giải thích cấp phiên dịch?

Nó có thể được lặp trong vòng lặp for, vì vậy nó dường như là mảng. Tuy nhiên, nó cũng có thể lặp lại hoàn toàn với đơn giản echo $@, nếu nó là một mảng, chỉ phần tử đầu tiên sẽ được hiển thị. Do giới hạn của shell, tôi không thể viết thêm mã thử nghiệm để thực hiện.

Sự khác biệt giữa bài đăng này : Bài đăng này cho thấy cách $@cư xử khác với $*. Nhưng tôi đang tự hỏi về các loại dữ liệu $@. Shell là ngôn ngữ thông dịch, như Python, nên biểu diễn dữ liệu theo một loạt các loại cơ bản. Hay nói cách khác, tôi muốn biết $ @ được lưu trong bộ nhớ máy tính như thế nào.

Nó là một chuỗi, một chuỗi nhiều dòng hoặc một mảng?

Nếu nó là một kiểu dữ liệu duy nhất, có thể định nghĩa một biến tùy chỉnh là một thể hiện của loại này không?


1

@Haxiel, tôi không nghĩ vậy, tôi đã viết sự khác biệt của họ ở cuối bài viết của tôi.
davmos

Bạn sẽ được phục vụ tốt hơn bằng cách kiểm tra sự khác biệt trong đầu ra với printf '%s\n' "$@"printf '%s\n' "$*". Các echotiện ích chỉ kết quả đầu ra đối số của nó, không có vấn đề nếu họ là một hoặc nhiều người. Cả hai đều là mảng (của chuỗi), nhưng chúng hoạt động khác nhau khi trích dẫn kép. Nếu một trong hai là một chuỗi nhiều dòng, thì họ sẽ không thể lưu trữ các chuỗi nhiều dòng (mà họ có thể). Không rõ vấn đề bạn đang cố gắng giải quyết.
Kusalananda

2
Câu hỏi của bạn tương đương với việc hỏi @varbiến là gì trong Perl, về mặt lưu trữ cơ bản của nó. Từ quan điểm của một chương trình Perl thông thường, nó không thực sự quan trọng, ngoài việc nó có thể truy cập dưới dạng một mảng / danh sách (và thực tế là có bối cảnh trong đó một danh sách được mong đợi).
Kusalananda

Câu trả lời:


16

Điều đó bắt đầu như một hack trong vỏ Bourne. Trong trình bao Bourne, việc tách từ IFS đã được thực hiện (sau khi mã hóa) trên tất cả các từ trong ngữ cảnh danh sách (đối số dòng lệnh hoặc các từ forlặp vòng lặp trên). Nếu bạn có:

IFS=i var=file2.txt
edit file.txt $var

Đó là dòng thứ hai sẽ được tokenised trong 3 từ, $varsẽ được mở rộng, và chia + glob sẽ được thực hiện trên tất cả ba chữ, vì vậy bạn sẽ kết thúc chạy edvới t, f, le.txt, f, le2.txtnhư các đối số.

Trích dẫn các phần của điều đó sẽ ngăn chặn sự phân chia + toàn cầu. Shell Bourne ban đầu nhớ các ký tự được trích dẫn bằng cách đặt bit thứ 8 vào bên trong chúng (điều này đã thay đổi sau đó khi Unix trở nên sạch 8bit, nhưng shell vẫn làm một cái gì đó tương tự như ghi nhớ byte nào được trích dẫn).

Cả hai $*$@là sự kết hợp của các tham số vị trí với không gian ở giữa. Nhưng có một xử lý đặc biệt $@khi bên trong dấu ngoặc kép. Nếu được $1chứa foo bar$2chứa baz, "$@"sẽ mở rộng thành:

foo bar baz
^^^^^^^ ^^^

(với ^s ở trên chỉ ra ký tự nào có tập bit thứ 8). Trong đó khoảng trắng đầu tiên được trích dẫn (có tập bit thứ 8) nhưng không phải là khoảng trống thứ hai (khoảng trắng được thêm vào giữa các từ).

Và đó là phân tách IFS đảm nhiệm việc tách các đối số (giả sử ký tự khoảng trắng nằm trong $IFSmặc định). Điều đó tương tự như cách $*mở rộng trong vỏ tiền thân của nó (vỏ dựa trên vỏ Thomson, trong khi vỏ Bourne được viết từ đầu).

Điều đó giải thích tại sao trong vỏ Bourne ban đầu "$@"sẽ mở rộng thành chuỗi rỗng thay vì không có gì cả khi danh sách các tham số vị trí trống (bạn phải làm việc với nó ${1+"$@"}), tại sao nó không giữ các tham số vị trí trống và tại sao "$@"không Sẽ không hoạt động khi $IFSkhông chứa ký tự khoảng trắng.

Ý định là có thể chuyển danh sách các đối số nguyên văn sang một lệnh khác, nhưng điều đó không hoạt động đúng cho danh sách trống, cho các phần tử trống hoặc khi $IFSkhông chứa khoảng trắng (hai vấn đề đầu tiên cuối cùng đã được sửa trong các phiên bản sau ).

Trình bao Korn (dựa trên thông số POSIX) đã thay đổi hành vi đó theo một số cách:

  • Việc chia IFS chỉ được thực hiện dựa trên kết quả của các mở rộng không được trích dẫn (không phải trên các từ nghĩa đen như edithoặc file.txttrong ví dụ trên)
  • $*$@được nối với ký tự đầu tiên của $IFShoặc khoảng $IFStrắng khi trống, ngoại trừ đối với dấu "$@"ngoặc kép, phép nối đó không được trích dẫn như trong trình bao Bourne và đối với dấu ngoặc kép "$*"khi IFStrống, các tham số vị trí được nối mà không có dấu phân cách.
  • nó đã thêm hỗ trợ cho các mảng và ${array[@]} ${array[*]}gợi nhớ đến Bourne $*$@bắt đầu từ chỉ số 0 thay vì 1 và thưa thớt (giống như mảng kết hợp) có nghĩa là $@không thể thực sự được coi là mảng ksh (so sánh với csh/ rc/ zsh/ fish/ yashtrong đó $argv/ $*là bình thường mảng).
  • Các yếu tố trống được bảo tồn.
  • "$@"khi $#0 là mở rộng thành không có gì thay vì chuỗi rỗng, "$@"hoạt động khi $IFSkhông chứa khoảng trắng trừ khi IFStrống. Một $*không được trích dẫn mà không có ký tự đại diện sẽ mở rộng thành một đối số (trong đó các tham số vị trí được nối với khoảng $IFStrắng ) khi trống.

ksh93 đã sửa một vài vấn đề còn lại ở trên. Trong ksh93, $*$@mở rộng ra danh sách các tham số vị trí, được phân tách bất kể giá trị của $IFS, và sau đó phân tách tiếp tục + nối tiếp + mở rộng trong ngữ cảnh danh sách, được $*nối với byte đầu tiên (không phải ký tự) của $IFS, "$@"trong bối cảnh danh sách mở rộng ra danh sách của các tham số vị trí, bất kể giá trị của $IFS. Trong ngữ cảnh không có danh sách, như trong var=$@, $@được nối với không gian bất kể giá trị của $IFS.

bashCác mảng được thiết kế sau các mảng ksh. Sự khác biệt là:

  • không mở rộng cú đúp khi mở rộng không trích dẫn
  • ký tự đầu tiên $IFSthay vì cho byte
  • một số khác biệt trường hợp góc như mở rộng $*khi không trích dẫn trong bối cảnh không có danh sách khi $IFStrống.

Mặc dù thông số POSIX trước đây khá mơ hồ, nhưng bây giờ ít nhiều chỉ định hành vi bash.

Nó khác với các mảng bình thường trong kshhoặc bashtrong đó:

  • Các chỉ số bắt đầu từ 1 thay vì 0 (ngoại trừ trong "${@:0}"đó bao gồm $0(không phải là tham số vị trí và trong các hàm cung cấp cho bạn tên của hàm hoặc không phụ thuộc vào trình bao và cách xác định hàm)).
  • Bạn không thể chỉ định các yếu tố riêng lẻ
  • nó không thưa thớt, bạn không thể hủy bỏ các yếu tố riêng lẻ
  • shift có thể được sử dụng.

Trong zshhoặc yashnơi các mảng là các mảng bình thường (không thưa thớt, các chỉ số bắt đầu giống như trong tất cả các shell khác nhưng ksh / bash), $*được coi là một mảng bình thường. zsh$argvmột bí danh cho nó (để tương thích với csh). $*là giống như $argvhoặc ${argv[*]}(các đối số được nối với ký tự đầu tiên của $IFSnhưng vẫn được tách ra trong ngữ cảnh danh sách). "$@"thích "${argv[@]}"hoặc "${*[@]}"}trải qua quá trình xử lý đặc biệt theo phong cách Korn.


8

Tuy nhiên, tôi không biết cấu trúc dữ liệu $@là gì.

Đó là một tham số đặc biệt mở rộng đến các giá trị của các tham số vị trí ... Nhưng đó là sự hiểu biết về thuật ngữ.

Chúng ta có thể xem các tham số vị trí như là một phần của $@, vì vậy nó có một số phần tử riêng biệt ( $1, $2...), có thể được truy cập độc lập và được đặt tên theo các số tự nhiên liên tiếp. Điều đó làm cho nó một cái gì đó thường được gọi là một mảng.

Cú pháp là một chút lạ, mặc dù, và thậm chí còn hạn chế. Không có cách nào để sửa đổi một phần tử riêng lẻ của mảng. Thay vào đó, toàn bộ điều phải được đặt cùng một lúc. (Bạn có thể sử dụng set -- "$@" foođể nối thêm một giá trị hoặc set -- "${@:1:2}" foo "${@:3}"để thêm một giá trị ở giữa. Nhưng trong cả hai trường hợp, bạn phải viết ra toàn bộ danh sách kết quả.)

Tại sao nó hành xử khác với $*khi bao gồm trong trích dẫn kép,

Bởi vì họ được định nghĩa để hành xử khác nhau.

Tuy nhiên, nó cũng có thể lặp lại hoàn toàn với đơn giản echo $@, nếu nó là một mảng, chỉ phần tử đầu tiên sẽ được hiển thị.

Nếu bạn có nghĩa là thực tế a=(foo bar asdf); echo $asẽ chỉ xuất ra foo, thì đây chủ yếu là một cú pháp của cú pháp shell và thực tế là các mảng có tên kiểu ksh được tạo muộn hơn các tham số vị trí và $@. Đồng bằng $agiống như ${a[0]}vậy vì vậy nó có ý nghĩa tương thích ngược của một giá trị vô hướng duy nhất, bất kể alà một mảng hay một biến vô hướng đơn giản.

Các @dấu hiệu ám chỉ đến toàn bộ danh sách đã được tái sử dụng với mảng có tên trong đó "${a[@]}"là cách để có được toàn bộ danh sách. So với các mảng được đặt tên, với $@, dấu ngoặc và dấu ngoặc không cần thiết và tên chỉ được bỏ qua.

Hay nói cách khác, tôi muốn biết cách $@lưu trữ trong bộ nhớ máy tính.

Điều đó phụ thuộc vào việc triển khai, bạn sẽ phải xem mã nguồn của bất kỳ shell cụ thể nào bạn quan tâm.

Nó là một chuỗi, một chuỗi nhiều dòng hoặc một mảng?

Một mảng, chủ yếu. Mặc dù khác với các mảng có tên kiểu ksh, vì chúng có thể có các số nguyên không âm tùy ý làm chỉ mục, không chỉ các số liên tiếp như với $@. (Nghĩa là, một mảng được đặt tên có thể thưa thớt và có các chỉ mục 1, 34, có 02thiếu. Điều đó là không thể với các tham số vị trí.)

Đây không phải là một chuỗi đơn, vì nó có thể được mở rộng thành các phần tử riêng biệt và việc gọi các dòng phần tử cũng không đúng, vì bất kỳ biến thông thường nào, hoặc một trong các tham số vị trí (phần tử của $@) cũng có thể chứa dòng mới.

Nếu nó là một kiểu dữ liệu duy nhất, có thể định nghĩa một biến tùy chỉnh là một thể hiện của loại này không?

Không. Nhưng các mảng được đặt tên có lẽ hữu ích hơn.


1
+1. TL: DR $@không phải là cấu trúc dữ liệu, nó là một trong một vài hàm / toán tử để mở rộng cấu trúc dữ liệu tham số vị trí.
Peter Cordes
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.