อัปเดต: บางคนบอกว่าไม่ควรใช้ eval ฉันไม่เห็นด้วย. eval
ผมคิดว่ามีความเสี่ยงที่เกิดขึ้นเมื่อป้อนข้อมูลเสียหายสามารถส่งผ่านไป อย่างไรก็ตามมีหลายสถานการณ์ทั่วไปที่ไม่เสี่ยงและดังนั้นจึงควรรู้วิธีใช้ eval ในกรณีใด ๆ คำตอบ stackoverflowนี้อธิบายถึงความเสี่ยงของการประเมินและทางเลือกในการประเมิน ในที่สุดมันก็ขึ้นอยู่กับผู้ใช้เพื่อตรวจสอบว่า / เมื่อ eval ปลอดภัยและมีประสิทธิภาพในการใช้
eval
คำสั่งbash อนุญาตให้คุณรันบรรทัดของโค้ดที่คำนวณหรือได้มาโดยสคริปต์ bash ของคุณ
บางทีตัวอย่างที่ตรงไปตรงมาที่สุดอาจเป็นโปรแกรมทุบตีที่เปิดสคริปต์ทุบตีอื่นเป็นไฟล์ข้อความอ่านแต่ละบรรทัดของข้อความและใช้eval
ในการดำเนินการตามลำดับ นั่นเป็นพฤติกรรมแบบเดียวกับsource
คำสั่งbash ซึ่งเป็นสิ่งที่เราจะใช้นอกเสียจากว่าจำเป็นต้องทำการแปลงรูปแบบบางอย่าง (เช่นการกรองหรือการแทนที่) กับเนื้อหาของสคริปต์ที่นำเข้า
ฉันไม่ค่อยต้องการeval
แต่ฉันพบว่ามีประโยชน์ในการอ่านหรือเขียนตัวแปรที่มีชื่ออยู่ในสตริงที่กำหนดให้กับตัวแปรอื่น ๆ ตัวอย่างเช่นในการดำเนินการกับชุดของตัวแปรในขณะที่รักษา footprint รหัสให้เล็กและหลีกเลี่ยงความซ้ำซ้อน
eval
เป็นแนวคิดที่เรียบง่าย อย่างไรก็ตามไวยากรณ์ที่เข้มงวดของภาษาทุบตีและคำสั่งการแยกวิเคราะห์ของ bash interpreter สามารถเหมาะสมและทำให้eval
ปรากฏเป็นความลับและยากที่จะใช้หรือเข้าใจ นี่คือสิ่งจำเป็น:
อาร์กิวเมนต์ที่ส่งผ่านไปยังeval
เป็นนิพจน์สตริงที่คำนวณเมื่อรันไทม์ eval
จะดำเนินการแยกวิเคราะห์ผลลัพธ์สุดท้ายของอาร์กิวเมนต์เป็นบรรทัดโค้ดจริงในสคริปต์ของคุณ
ไวยากรณ์และคำสั่งแยกวิเคราะห์มีความเข้มงวด หากผลลัพธ์ไม่ใช่บรรทัดที่ใช้งานได้ของโค้ดทุบตีในขอบเขตของสคริปต์ของคุณโปรแกรมจะทำงานล้มเหลวในeval
คำสั่งเนื่องจากพยายามเรียกใช้ขยะ
เมื่อทดสอบคุณสามารถแทนที่eval
คำสั่งด้วยecho
และดูสิ่งที่จะปรากฏ หากเป็นรหัสที่ถูกต้องในบริบทปัจจุบันการเรียกใช้ผ่านeval
จะทำงานได้
ตัวอย่างต่อไปนี้อาจช่วยให้ชัดเจนว่า eval ทำงานอย่างไร ...
ตัวอย่างที่ 1:
eval
คำสั่งหน้ารหัส 'ปกติ' คือ NOP
$ eval a=b
$ eval echo $a
b
ในตัวอย่างข้างต้นeval
คำสั่งแรกไม่มีวัตถุประสงค์และสามารถตัดออกได้ eval
ไม่มีจุดหมายในบรรทัดแรกเนื่องจากไม่มีการกำหนดโค้ดแบบไดนามิกนั่นคือมันแยกวิเคราะห์แล้วในบรรทัดสุดท้ายของรหัสทุบตีดังนั้นมันจะเหมือนกับคำสั่งปกติของรหัสในสคริปต์ทุบตี ลำดับที่ 2 eval
นั้นไม่มีจุดหมายเช่นกันเพราะถึงแม้ว่าจะมีขั้นตอนการแยกวิเคราะห์เป็น$a
สตริงตัวอักษรที่เทียบเท่ากัน แต่ก็ไม่มีทางอ้อม (เช่นไม่มีการอ้างอิงผ่านค่าสตริงของคำนาม bash จริงหรือตัวแปรสคริปต์ที่ถือ bash) ดังนั้นมันจะทำงานเหมือนกัน เป็นบรรทัดของรหัสโดยไม่มีeval
คำนำหน้า
ตัวอย่างที่ 2:
ดำเนินการกำหนด var โดยใช้ชื่อ var ที่ส่งผ่านเป็นค่าสตริง
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
หากคุณต้องการecho $key=$val
ผลลัพธ์จะเป็น:
mykey=myval
นั่นคือผลลัพธ์สุดท้ายของการแยกสตริงคือสิ่งที่จะถูกประมวลผลโดย eval ดังนั้นผลลัพธ์ของคำสั่ง echo ในตอนท้าย ...
ตัวอย่างที่ 3:
การเพิ่มทางอ้อมให้กับตัวอย่างที่ 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
ข้างต้นเป็นบิตที่ซับซ้อนกว่าตัวอย่างก่อนหน้านี้อาศัยอย่างหนักในการแยกคำสั่งและลักษณะของการทุบตี eval
บรรทัดประมาณจะได้รับการแยกวิเคราะห์ภายในในลำดับต่อไปนี้(หมายเหตุงบต่อไปนี้ pseudocode ไม่รหัสจริงเพียงแค่พยายามที่จะแสดงให้เห็นว่าคำสั่งที่จะได้รับแบ่งออกเป็นขั้นตอนภายในที่จะมาถึงผลสุดท้าย)
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
หากคำสั่งการแยกวิเคราะห์ที่สันนิษฐานไม่ได้อธิบายว่า eval ทำอะไรได้พอตัวอย่างที่สามอาจอธิบายการแยกวิเคราะห์อย่างละเอียดเพื่อช่วยอธิบายสิ่งที่เกิดขึ้น
ตัวอย่างที่ 4:
ค้นพบว่า vars ซึ่งมีชื่ออยู่ในสตริงมีค่าสตริงหรือไม่
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
ในการทำซ้ำครั้งแรก:
varname="myvarname_a"
Bash วิเคราะห์ข้อโต้แย้งeval
และeval
เห็นอย่างนี้เมื่อรันไทม์:
eval varval=\$$myvarname_a
ต่อไปนี้pseudocodeความพยายามที่จะแสดงให้เห็นถึงวิธีการตีความทุบตีบรรทัดข้างต้นของจริงeval
รหัสที่จะมาถึงค่าสุดท้ายที่ดำเนินการโดย (บรรทัดอธิบายต่อไปนี้ไม่ใช่รหัสทุบตีที่แน่นอน):
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)
เมื่อแยกวิเคราะห์เสร็จแล้วผลลัพธ์ก็คือสิ่งที่ถูกดำเนินการและผลกระทบที่เห็นได้ชัดแสดงให้เห็นว่าไม่มีอะไรลึกลับโดยเฉพาะเกี่ยวกับeval
ตัวเองและความซับซ้อนอยู่ในการแยกวิเคราะห์ของมัน
varval="User-provided"
รหัสที่เหลืออยู่ในตัวอย่างด้านบนจะทำการทดสอบเพื่อดูว่าค่าที่กำหนดให้กับ $ varval เป็นโมฆะหรือไม่และหากเป็นเช่นนั้นจะแจ้งให้ผู้ใช้ระบุค่า
$($n)
ทำงาน$n
ใน subshell มันพยายามรันคำสั่ง1
ที่ไม่มีอยู่