Ngoài các mảng kết hợp, có một số cách để đạt được các biến động trong Bash. Lưu ý rằng tất cả các kỹ thuật này có rủi ro, được thảo luận ở phần cuối của câu trả lời này.
Trong các ví dụ sau tôi sẽ giả sử rằng i=37
và bạn muốn đặt bí danh cho biến có tên var_37
giá trị ban đầu là lolilol
.
Phương pháp 1. Sử dụng biến con trỏ của người dùng
Bạn có thể chỉ cần lưu trữ tên của biến trong một biến không xác định, không giống như một con trỏ C. Bash sau đó có một cú pháp để đọc biến bí danh: ${!name}
mở rộng đến giá trị của biến có tên là giá trị của biến name
. Bạn có thể nghĩ về nó như một bản mở rộng hai giai đoạn: ${!name}
mở rộng tới $var_37
, mở rộng ra lolilol
.
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
Thật không may, không có cú pháp đối tác để sửa đổi biến bí danh. Thay vào đó, bạn có thể đạt được sự phân công với một trong những thủ thuật sau.
1a. Chỉ định vớieval
eval
là xấu xa, nhưng cũng là cách đơn giản và dễ mang theo nhất để đạt được mục tiêu của chúng tôi. Bạn phải cẩn thận thoát khỏi phía bên phải của bài tập, vì nó sẽ được đánh giá hai lần . Một cách dễ dàng và có hệ thống để làm điều này là đánh giá phía bên tay phải trước (hoặc sử dụng printf %q
).
Và bạn nên kiểm tra thủ công rằng phía bên trái là tên biến hợp lệ hoặc tên có chỉ mục (nếu đó là evil_code #
gì?). Ngược lại, tất cả các phương pháp khác dưới đây thực thi nó tự động.
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
Nhược điểm:
- không kiểm tra tính hợp lệ của tên biến.
eval
là xấu xa
eval
là xấu xa
eval
là xấu xa
1b. Chỉ định vớiread
Nội dung read
cho phép bạn gán giá trị cho một biến mà bạn đặt tên, một thực tế có thể được khai thác cùng với chuỗi ở đây:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
Phần IFS
và tùy chọn -r
đảm bảo rằng giá trị được gán nguyên trạng, trong khi tùy chọn -d ''
cho phép gán giá trị nhiều dòng. Do tùy chọn cuối cùng này, lệnh trả về với mã thoát khác không.
Lưu ý rằng, vì chúng tôi đang sử dụng chuỗi ở đây, một ký tự dòng mới được thêm vào giá trị.
Nhược điểm:
- hơi mơ hồ;
- trả về với mã thoát khác không;
- nối thêm một dòng mới vào giá trị
1c. Chỉ định vớiprintf
Kể từ Bash 3.1 (phát hành năm 2005), printf
nội dung cũng có thể gán kết quả của nó cho một biến có tên được đặt. Ngược lại với các giải pháp trước đó, nó chỉ hoạt động, không cần nỗ lực thêm để thoát khỏi mọi thứ, để ngăn chặn sự chia tách và như vậy.
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
Nhược điểm:
Phương pháp 2. Sử dụng biến tham chiếu của người Viking
Kể từ Bash 4.3 (phát hành năm 2014), declare
nội dung dựng sẵn có một tùy chọn -n
để tạo một biến là tham chiếu tên của tên Cameron đến một biến khác, giống như các tham chiếu C ++. Giống như trong Phương pháp 1, tham chiếu lưu tên của biến bí danh, nhưng mỗi lần tham chiếu được truy cập (để đọc hoặc gán), Bash sẽ tự động giải quyết vấn đề.
Ngoài ra, Bash có một cú pháp đặc biệt và rất khó hiểu để có được giá trị của chính tham chiếu, hãy tự đánh giá : ${!ref}
.
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
Điều này không tránh được những cạm bẫy được giải thích dưới đây, nhưng ít nhất nó làm cho cú pháp đơn giản.
Nhược điểm:
Rủi ro
Tất cả các kỹ thuật răng cưa này có một số rủi ro. Cái đầu tiên là thực thi mã tùy ý mỗi khi bạn giải quyết được chỉ định (để đọc hoặc để gán) . Thật vậy, thay vì một tên biến vô hướng, như var_37
, bạn cũng có thể đặt bí danh cho một mảng con, như arr[42]
. Nhưng Bash đánh giá nội dung của dấu ngoặc vuông mỗi khi cần, do đó, việc khử răng cưa arr[$(do_evil)]
sẽ có tác dụng không mong muốn. Do đó, chỉ sử dụng các kỹ thuật này khi bạn kiểm soát nguồn gốc của bí danh .
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
Rủi ro thứ hai là tạo ra một bí danh tuần hoàn. Vì các biến Bash được xác định bằng tên của chúng chứ không phải theo phạm vi của chúng, bạn có thể vô tình tạo một bí danh cho chính nó (trong khi nghĩ rằng nó sẽ bí danh một biến từ một phạm vi kèm theo). Điều này có thể xảy ra đặc biệt khi sử dụng tên biến phổ biến (như var
). Do đó, chỉ sử dụng các kỹ thuật này khi bạn kiểm soát tên của biến bí danh .
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
Nguồn: