Cập nhật: Một số người nói rằng mọi người nên sử dụng eval. Tôi không đồng ý. Tôi nghĩ rằng rủi ro phát sinh khi đầu vào tham nhũng có thể được chuyển qua eval
. Tuy nhiên, có nhiều tình huống phổ biến trong đó không phải là rủi ro, và do đó, đáng để biết cách sử dụng eval trong mọi trường hợp. Câu trả lời stackoverflow này giải thích các rủi ro của eval và các lựa chọn thay thế cho eval. Cuối cùng, tùy thuộc vào người dùng để xác định xem / khi eval an toàn và hiệu quả để sử dụng.
Câu eval
lệnh bash cho phép bạn thực thi các dòng mã được tính hoặc thu được, bằng tập lệnh bash của bạn.
Có lẽ ví dụ đơn giản nhất sẽ là một chương trình bash mở một tập lệnh bash khác dưới dạng tệp văn bản, đọc từng dòng văn bản và sử dụng eval
để thực hiện chúng theo thứ tự. Về cơ bản đó là hành vi tương tự như source
câu lệnh bash , đó là những gì người ta sẽ sử dụng, trừ khi cần phải thực hiện một số loại chuyển đổi (ví dụ: lọc hoặc thay thế) trên nội dung của tập lệnh được nhập.
Tôi hiếm khi cần eval
, nhưng tôi thấy hữu ích khi đọc hoặc viết các biến có tên được chứa trong các chuỗi được gán cho các biến khác. Ví dụ, để thực hiện các hành động trên các bộ biến, trong khi giữ cho dấu chân mã nhỏ và tránh dư thừa.
eval
là khái niệm đơn giản. Tuy nhiên, cú pháp chặt chẽ của ngôn ngữ bash và thứ tự phân tích cú pháp của trình thông dịch bash có thể bị thay đổi và làm cho nó trở nên eval
khó hiểu và khó sử dụng hoặc khó hiểu. Dưới đây là những điều cần thiết:
Đối số được truyền vào eval
là một biểu thức chuỗi được tính toán trong thời gian chạy. eval
sẽ thực hiện kết quả được phân tích cú pháp cuối cùng của đối số dưới dạng một dòng mã thực tế trong tập lệnh của bạn.
Cú pháp và thứ tự phân tích cú pháp là nghiêm ngặt. Nếu kết quả không phải là dòng mã bash có thể thực thi được, trong phạm vi tập lệnh của bạn, chương trình sẽ bị lỗi trên eval
câu lệnh khi nó cố gắng thực thi rác.
Khi kiểm tra, bạn có thể thay thế eval
câu lệnh echo
và xem những gì được hiển thị. Nếu đó là mã hợp pháp trong bối cảnh hiện tại, chạy nó eval
sẽ hoạt động.
Các ví dụ sau đây có thể giúp làm rõ cách thức hoạt động của eval ...
Ví dụ 1:
eval
tuyên bố trước mã 'bình thường' là một NOP
$ eval a=b
$ eval echo $a
b
Trong ví dụ trên, các eval
tuyên bố đầu tiên không có mục đích và có thể được loại bỏ. eval
là vô nghĩa trong dòng đầu tiên vì không có khía cạnh động đối với mã, tức là nó đã được phân tích cú pháp thành dòng cuối cùng của mã bash, do đó nó sẽ giống hệt như một câu lệnh mã thông thường trong tập lệnh bash. Thứ 2 eval
cũng vô nghĩa, bởi vì, mặc dù có một bước phân tích chuyển đổi $a
thành chuỗi ký tự tương đương của nó, nhưng không có sự gián tiếp (ví dụ: không tham chiếu qua giá trị chuỗi của một danh từ bash thực tế hoặc biến tập lệnh bash), do đó, nó sẽ hoạt động giống hệt như một dòng mã không có eval
tiền tố.
Ví dụ 2:
Thực hiện gán var bằng cách sử dụng tên var được truyền dưới dạng giá trị chuỗi.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Nếu bạn đã echo $key=$val
, đầu ra sẽ là:
mykey=myval
Rằng , là kết quả cuối cùng của phân tích chuỗi, là những gì sẽ được thực thi bởi eval, do đó là kết quả của câu lệnh echo ở cuối ...
Ví dụ 3:
Thêm nhiều hướng dẫn hơn vào ví dụ 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Ở trên là một chút phức tạp hơn so với ví dụ trước, phụ thuộc nhiều hơn vào thứ tự phân tích cú pháp và đặc thù của bash. Các eval
dòng sẽ xấp xỉ được phân tích nội bộ theo thứ tự sau (lưu ý những điều khoản sau đây là giả, không phải mã thực, chỉ cần cố gắng để hiển thị như thế nào tuyên bố sẽ được chia nhỏ thành các bước trong nội bộ để đi đến kết quả cuối cùng) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Nếu thứ tự phân tích giả định không giải thích những gì eval đang làm đủ, ví dụ thứ ba có thể mô tả phân tích chi tiết hơn để giúp làm rõ những gì đang diễn ra.
Ví dụ 4:
Khám phá xem các vars, có tên được chứa trong chuỗi, bản thân chúng có chứa các giá trị chuỗi hay không.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
Trong lần lặp đầu tiên:
varname="myvarname_a"
Bash phân tích cú pháp đối số eval
và eval
thấy nghĩa đen này trong thời gian chạy:
eval varval=\$$myvarname_a
Mã giả sau đây cố gắng minh họa cách bash diễn giải dòng trên của mã thực , để đến giá trị cuối cùng được thực thi bởi eval
. (các dòng mô tả sau đây, không phải mã bash chính xác):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Một khi tất cả các phân tích cú pháp được thực hiện, kết quả là những gì được thực thi và hiệu quả của nó là rõ ràng, chứng tỏ không có gì đặc biệt bí ẩn về eval
chính nó, và sự phức tạp nằm trong phân tích đối số của nó.
varval="User-provided"
Mã còn lại trong ví dụ trên chỉ đơn giản là kiểm tra xem giá trị được gán cho $ varval có phải là null không và nếu có, sẽ nhắc người dùng cung cấp giá trị.
$($n)
chạy$n
trong một subshell. Nó cố chạy lệnh1
không tồn tại.