Bash có cung cấp hỗ trợ cho việc sử dụng con trỏ không?


12

Câu hỏi đơn giản. Liệu bash shell có hỗ trợ nào cho việc sử dụng con trỏ khi viết tập lệnh shell không?

Tôi quen thuộc với ký hiệu mở rộng, ${var[@]}khi lặp qua mảng $var, nhưng không rõ điều này là sử dụng các con trỏ để lặp qua các chỉ số mảng. Bash có cung cấp quyền truy cập vào các địa chỉ bộ nhớ như các ngôn ngữ khác không?

Nếu bash không hỗ trợ sử dụng con trỏ, các shell khác làm gì?

Câu trả lời:


28

Một con trỏ (đến một vị trí của bộ nhớ ) không thực sự là một khái niệm hữu ích ở bất kỳ mức nào cao hơn C, có thể là một cái gì đó giống như Python hoặc shell. Các tham chiếu đến các đối tượng tất nhiên là hữu ích trong các ngôn ngữ cấp cao, thậm chí có thể cần thiết để xây dựng các cấu trúc dữ liệu phức tạp. Nhưng trong hầu hết các trường hợp, suy nghĩ về các địa chỉ bộ nhớ là mức quá thấp sẽ rất hữu ích.

Trong Bash (và các shell khác), bạn có thể nhận các giá trị của các phần tử mảng với ${array[index]}ký hiệu, gán chúng với array[index]=...và lấy số phần tử trong mảng với ${#array[@]}. Biểu thức bên trong ngoặc là biểu thức số học. Để làm ví dụ, chúng ta có thể thêm tiền tố không đổi cho tất cả các thành viên mảng:

for ((i=0 ; i < ${#array[@]} ; i++ )) ; do
    array[i]="foo-${array[i]}"
done

(Nếu chúng ta chỉ quan tâm đến các giá trị, và không phải các chỉ mục, for x in "${array[@]}" ; do...sẽ ổn thôi.)

Với các mảng kết hợp hoặc thưa thớt , một vòng số không có ý nghĩa gì nhiều, nhưng thay vào đó chúng ta cần tìm nạp các khóa / chỉ mục mảng bằng ${!array[@]}. Ví dụ

declare -A assoc=([foo]="123" [bar]="456")
for i in "${!assoc[@]}" ; do 
    echo "${assoc[$i]}"
done 

Thêm vào đó, Bash có hai cách để gián tiếp trỏ đến một biến khác:

  • mở rộng gián tiếp , sử dụng các ${!var}cú pháp , trong đó sử dụng các giá trị của biến có tên là trong var, và
  • namerefs , cần được tạo bằng declarenội trang (hoặc kshtừ đồng nghĩa tương thích, typeset). declare -n ref=varlàm refmột tham chiếu đến biến var.

Namerefs cũng hỗ trợ lập chỉ mục, trong đó nếu chúng ta có arr=(a b c); declare -n ref=arr;thì ${ref[1]}sẽ mở rộng sang b. Việc sử dụng ${!p[1]}thay vào đó sẽ plà một mảng và tham chiếu đến biến được đặt tên bởi phần tử thứ hai của nó.

Trong Bash, namerefs theo nghĩa đen là, tham chiếu theo tên và sử dụng một nameref từ bên trong một hàm sẽ sử dụng giá trị cục bộ của biến được đặt tên. Điều này sẽ in local value of var.

#!/bin/bash
fun() {
        local var="local value of var"
        echo "$ref";
}
var="global var"
declare -n ref=var
fun

BashFAQ cũng có một bài viết dài hơn về sự gián tiếp .


2
indirection khá hữu ích trong các ngôn ngữ cấp cao hơn. ví dụ: tài liệu tham khảo trong perl. Chúng không giống như con trỏ C nhưng chúng phục vụ cùng chức năng cơ bản. Ngay cả bash cũng có thể truy cập các biến một cách gián tiếp ... nhưng IMO nếu bạn bắt đầu viết mã sử dụng đáng kể tính năng này, tốt hơn hết bạn nên bắt đầu từ đầu với perl hoặc một cái gì đó. Xem thêm mywiki.wooledge.org/BashFAQ/006
cas

2
@cas, oh, hoàn toàn. Nhưng có lẽ tốt hơn khi nghĩ về chúng như chỉ vào các đối tượng , thay vì địa chỉ bộ nhớ. (Ngay cả trong C, có một loại liên quan.) Tôi muốn lưu ý cả mở rộng gián tiếp và namerefs, nhưng không có thời gian để làm điều đó ngay lập tức.
ilkkachu

Có lẽ đáng để chỉ ra rằng ví dụ về vòng lặp được viết tự nhiên hơn for foo in "${array[@]}" ; do ... donetrừ khi bạn cần chỉ mục cho (các) mục đích khác.
Will Crawford

@WillCrawford, điểm. chỉnh sửa ví dụ và ghi chú.
ilkkachu

9

Không, bashkhông có "con trỏ", nhưng nó có tài liệu tham khảo:

$ spam="fred"
$ declare -n tripe=spam
$ echo $tripe
fred
$ tripe=juki
$ echo $spam
juki

Từ bashtrang người đàn ông:

Một biến có thể được gán thuộc tính nameref bằng cách sử dụng tùy chọn -n cho các lệnh declarehoặc locallệnh dựng sẵn để tạo nameref hoặc tham chiếu đến biến khác. Điều này cho phép các biến được thao tác gián tiếp. Bất cứ khi nào biến nameref được tham chiếu, gán cho, bỏ đặt hoặc sửa đổi các thuộc tính của nó (ngoài việc sử dụng hoặc thay đổi thuộc tính nameref), hoạt động thực sự được thực hiện trên biến được chỉ định bởi giá trị của biến nameref. Một nameref thường được sử dụng trong các hàm shell để chỉ một biến có tên được truyền dưới dạng đối số cho hàm. Chẳng hạn, nếu một tên biến được truyền cho hàm shell làm đối số đầu tiên của nó, thì chạy

declare -n ref=$1

bên trong hàm tạo ra một biến nameref ref có giá trị là tên biến được truyền làm đối số đầu tiên. Các tham chiếu và phép gán cho ref và thay đổi các thuộc tính của nó, được coi là các tham chiếu, phép gán và sửa đổi thuộc tính cho biến có tên được truyền là $ 1. Nếu biến điều khiển trong vòng lặp for có thuộc tính nameref, danh sách các từ có thể là danh sách các biến shell và tham chiếu tên sẽ được thiết lập cho từng từ trong danh sách, khi đó, vòng lặp được thực thi. Các biến mảng không thể được đưa ra thuộc tính nameref. Tuy nhiên, các biến nameref có thể tham chiếu các biến mảng và biến mảng được đăng ký. Namerefs có thể được bỏ đặt bằng cách sử dụng tùy chọn -n cho unsetnội dung. Nếu không, nếuunset được thực thi với tên của biến nameref làm đối số, biến được tham chiếu bởi biến nameref sẽ không được đặt.


4

Không, shell không sử dụng "con trỏ" (như được hiểu trong C).

Mảng có thể sử dụng các chỉ mục: echo "${array[2]}"nhưng @trong ví dụ của bạn không thực sự là "con trỏ". Đó là một cách để thể hiện "danh sách các giá trị mảng". Một cái gì đó mà trình phân tích cú pháp shell hiểu. Tương tự như cách a:

$ echo "$@"

mở rộng ra tất cả danh sách "Thông số vị trí".


2

Trong khi các mảng số nguyên bash có thể được định nghĩa & truy cập lặp đi lặp lại như vậy;

declare -a obj
obj+=("val1")
obj+=("val2")

for item in ${obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Các mảng được lập chỉ mục dựa trên chuỗi hoặc chuỗi trong bash yêu cầu định nghĩa lặp sau;

declare -A obj
obj["key1"]="val1"
obj["key2"]="val2"

for item in ${!obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Để trả lời câu hỏi liên quan đến con trỏ và sử dụng một từ bash; chức năng bên trong của nhị phân bash đã biên dịch thực sự sử dụng các con trỏ tới bộ nhớ được phân bổ trên ngăn xếp và không làm lộ chức năng tương tự khi sử dụng eval. Xem [tài liệu tham khảo gián tiếp] http://tldp.org/LDP/abs/html/ivr.html )

Có rồng; sử dụng evalnên được sử dụng một cách thận trọng do ý nghĩa bảo mật

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.