วิธีใช้ eval ใน Linux Bash Scripts
เผยแพร่แล้ว: 2022-08-22 จากคำสั่ง Bash ทั้งหมด eval
ที่เก่าไม่ดีอาจมีชื่อเสียงที่แย่ที่สุด มีเหตุผลหรือเพียงแค่กดไม่ดี? เราพูดถึงการใช้งานและอันตรายของคำสั่ง Linux ที่ไม่ค่อยมีใครรู้จัก
เราต้องการพูดคุยเกี่ยวกับ eval
ใช้อย่างไม่ระมัดระวัง eval
สามารถนำไปสู่พฤติกรรมที่คาดเดาไม่ได้และแม้กระทั่งความไม่มั่นคงของระบบ ดูจากเสียงแล้วเราคงไม่ควรใช้ใช่มั้ยครับ? ก็ไม่เชิงหรอก
คุณสามารถพูดบางอย่างที่คล้ายกันเกี่ยวกับรถยนต์ได้ ในมือผิด พวกมันคืออาวุธร้ายแรง ผู้คนใช้พวกมันในการบุกจู่โจมและเป็นพาหนะหลบหนี เราทุกคนควรหยุดใช้รถยนต์หรือไม่? ไม่แน่นอนไม่ แต่ต้องใช้อย่างถูกวิธีและโดยคนที่รู้วิธีขับ
คำคุณศัพท์ทั่วไปที่ใช้กับ eval
คือ "ชั่วร้าย" แต่ทั้งหมดอยู่ที่วิธีการใช้ คำสั่ง eval
เปรียบเทียบ ค่า จากตัวแปรตั้งแต่หนึ่งตัวขึ้นไป มันสร้างสตริงคำสั่ง จากนั้นรันคำสั่งนั้น สิ่งนี้ทำให้มีประโยชน์เมื่อคุณต้องการรับมือกับสถานการณ์ที่เนื้อหาของคำสั่งได้รับมาแบบไดนามิกในระหว่างการเรียกใช้สคริปต์ของคุณ
ปัญหาเกิดขึ้นเมื่อสคริปต์ถูกเขียนขึ้นเพื่อใช้ eval
บนสตริงที่ได้รับจากที่อื่น นอก สคริปต์ ผู้ใช้อาจพิมพ์ข้อความนี้ ส่งผ่าน API ติดแท็กในคำขอ HTTPS หรือที่อื่นๆ ภายนอกสคริปต์
หากสตริงที่ใช้ eval
ทำงานไม่ได้มาจากภายในและโดยทางโปรแกรม มีความเสี่ยงที่สตริงนั้นอาจมีคำสั่งที่เป็นอันตรายที่ฝังอยู่หรืออินพุตที่มีรูปแบบไม่ดีอื่นๆ แน่นอน คุณไม่ต้องการให้ eval
รันคำสั่งที่เป็นอันตราย เพื่อความปลอดภัย อย่าใช้ eval
กับสตริงที่สร้างจากภายนอกหรืออินพุตของผู้ใช้
ก้าวแรกกับ eval
คำสั่ง eval
เป็นคำสั่งเชลล์ Bash ในตัว หากมี Bash แสดงว่ามี eval
eval
เชื่อมพารามิเตอร์ต่างๆ เข้าด้วยกันเป็นสตริงเดียว จะใช้ช่องว่างเดียวเพื่อแยกองค์ประกอบที่ต่อกัน จะประเมินอาร์กิวเมนต์แล้วส่งผ่านสตริงทั้งหมดไปยังเชลล์เพื่อดำเนินการ
มาสร้างตัวแปรที่เรียกว่า wordcount
กัน
wordcount="wc -w raw-notes.md"
ตัวแปรสตริงประกอบด้วยคำสั่งนับจำนวนคำในไฟล์ชื่อ “raw-notes.md”
เราสามารถใช้ eval
เพื่อรันคำสั่งนั้นได้โดยส่ง ค่า ของตัวแปรไปให้
eval " $wordcount "
คำสั่งดำเนินการในเชลล์ปัจจุบัน ไม่ใช่ในเชลล์ย่อย เราสามารถแสดงสิ่งนี้ได้อย่างง่ายดาย เรามีไฟล์ข้อความสั้นชื่อ “variables.txt” ประกอบด้วยสองบรรทัดนี้
ครั้งแรก=วิธีการ วินาที = Geek
เราจะใช้ cat
เพื่อส่งบรรทัดเหล่านี้ไปยังหน้าต่างเทอร์มินัล จากนั้นเราจะใช้ eval
เพื่อประเมินคำสั่ง cat
เพื่อให้ดำเนินการตามคำสั่งภายในไฟล์ข้อความ สิ่งนี้จะกำหนดตัวแปรให้เรา
cat variables.txt eval "$(ตัวแปร cat.txt)" echo $first $วินาที
โดยใช้ echo
เพื่อพิมพ์ค่าของตัวแปร เราจะเห็นว่าคำสั่ง eval
ทำงานในเชลล์ปัจจุบัน ไม่ใช่ subshell
กระบวนการในเชลล์ย่อยไม่สามารถเปลี่ยนสภาพแวดล้อมของเชลล์ของพาเรนต์ได้ เนื่องจาก eval รันในเชลล์ปัจจุบัน ตัวแปรที่กำหนดโดย eval
จึงสามารถใช้งานได้จากเชลล์ที่เรียกใช้คำสั่ง eval
โปรดทราบว่าหากคุณใช้ eval
ในสคริปต์ เชลล์ที่จะแก้ไขโดย eval
จะเป็นเชลล์ย่อยที่สคริปต์กำลังทำงานอยู่ ไม่ใช่เชลล์ที่เปิดใช้งาน
ที่เกี่ยวข้อง: วิธีใช้คำสั่ง Linux cat และ tac
การใช้ตัวแปรใน Command String
เราสามารถรวมตัวแปรอื่นๆ ในสตริงคำสั่งได้ เราจะตั้งค่าสองตัวแปรเพื่อเก็บจำนวนเต็ม
num1=10 num2=7
เราจะสร้างตัวแปรเพื่อเก็บคำสั่ง expr
ซึ่งจะคืนค่าผลรวมของตัวเลขสองตัว ซึ่งหมายความว่าเราจำเป็นต้องเข้าถึงค่าของตัวแปรจำนวนเต็มสองตัวในคำสั่ง สังเกต backticks รอบคำสั่ง expr
add="`expr $num1 + $num2`"
เราจะสร้างคำสั่งอื่นเพื่อแสดงผลลัพธ์ของคำสั่ง expr
แสดง = "สะท้อน"
โปรดทราบว่าเราไม่จำเป็นต้องเว้นวรรคที่ส่วนท้ายของสตริง echo
หรือที่จุดเริ่มต้นของสตริง expr
eval
ดูแลสิ่งนั้น
และเพื่อดำเนินการคำสั่งทั้งหมดที่เราใช้:
ประเมิน $แสดง $add
ค่าตัวแปรภายในสตริง expr
จะถูกแทนที่ลงในสตริงด้วย eval
ก่อนที่จะส่งผ่านไปยังเชลล์เพื่อดำเนินการ
ที่เกี่ยวข้อง: วิธีทำงานกับตัวแปรใน Bash
การเข้าถึงตัวแปรภายในตัวแปร
คุณสามารถกำหนดค่าให้กับตัวแปร แล้วกำหนด ชื่อ ของตัวแปรนั้นให้กับตัวแปรอื่นได้ เมื่อใช้ eval
คุณสามารถเข้าถึง ค่า ที่อยู่ในตัวแปรตัวแรก จากชื่อของมันซึ่งเป็น ค่า ที่เก็บไว้ในตัวแปรที่สอง ตัวอย่างจะช่วยให้คุณแก้ให้หายยุ่งได้ว่า
คัดลอกสคริปต์นี้ไปยังโปรแกรมแก้ไข และบันทึกเป็นไฟล์ชื่อ “assign.sh”
#!/bin/bash title="How-To Geek" หน้าเว็บ=ชื่อเรื่อง command="echo" eval $command \${$หน้าเว็บ}
เราต้องทำให้มันสามารถเรียกใช้งานได้ด้วยคำสั่ง chmod
chmod +x assign.sh
คุณจะต้องทำเช่นนี้กับสคริปต์ที่คุณคัดลอกจากบทความนี้ เพียงใช้ชื่อสคริปต์ที่เหมาะสมในแต่ละกรณี
เมื่อเราเรียกใช้สคริปต์ เราจะเห็นข้อความจาก title
ตัวแปรแม้ว่าคำสั่ง eval
จะใช้ webpage
ตัวแปร
./assign.sh
เครื่องหมายดอลลาร์ที่เป็นอักขระหลีก “ $
” และเครื่องหมายวงเล็บปีกกา “ {}
” ทำให้ eval ดูค่าที่เก็บไว้ภายในตัวแปรที่มีชื่อเก็บอยู่ในตัวแปรของ webpage
การใช้ตัวแปรที่สร้างขึ้นแบบไดนามิก
เราสามารถใช้ eval
เพื่อสร้างตัวแปรแบบไดนามิก สคริปต์นี้เรียกว่า "loop.sh"
#!/bin/bash รวม=0 label="การวนซ้ำเสร็จสมบูรณ์ ทั้งหมด:" สำหรับ n ใน {1..10} ทำ ค่า x$n=$n echo "วนซ้ำ" $x$n ((รวม+=$x$n)) เสร็จแล้ว เสียงสะท้อน $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10 echo $label $total
มันสร้างตัวแปรที่เรียกว่า total
ซึ่งเก็บผลรวมของค่าของตัวแปรที่เราสร้าง จากนั้นจะสร้างตัวแปรสตริงที่เรียกว่า label
นี่เป็นสตริงข้อความง่ายๆ
เราจะวนซ้ำ 10 ครั้งและสร้าง 10 ตัวแปรที่เรียกว่า x1
up to x10
คำสั่ง eval
ในเนื้อความของลูปให้ "x" และใช้ค่าของตัวนับลูป $n
เพื่อสร้างชื่อตัวแปร ในเวลาเดียวกัน มันตั้งค่าตัวแปรใหม่เป็นค่าของตัวนับลูป $n
มันพิมพ์ตัวแปรใหม่ไปที่หน้าต่างเทอร์มินัลแล้วเพิ่มตัวแปร total
ด้วยค่าของตัวแปรใหม่
นอกลูป ตัวแปรใหม่ 10 ตัวจะถูกพิมพ์อีกครั้ง ทั้งหมดในบรรทัดเดียว โปรดทราบว่าเราสามารถอ้างถึงตัวแปรด้วยชื่อจริงของตัวแปรได้เช่นกัน โดยไม่ต้องใช้ชื่อที่คำนวณหรือได้รับมา
สุดท้าย เราพิมพ์ค่าของตัวแปร total
./loop.sh
ที่เกี่ยวข้อง: ไพรเมอร์: Bash Loops: สำหรับในขณะที่และจนถึง
การใช้ eval กับ Arrays
ลองนึกภาพสถานการณ์ที่คุณมีสคริปต์ที่ใช้เวลานานและดำเนินการประมวลผลให้คุณ มันเขียนไปยังไฟล์บันทึกที่มีชื่อที่สร้างจากการประทับเวลา ในบางครั้ง ไฟล์บันทึกจะเริ่มขึ้นใหม่ เมื่อสคริปต์เสร็จสิ้น หากไม่มีข้อผิดพลาด สคริปต์จะลบไฟล์บันทึกที่สร้างขึ้น
คุณไม่ต้องการให้มันเป็นเพียงแค่ rm *.log
คุณเพียงแค่ต้องการให้มันลบไฟล์บันทึกที่สร้างขึ้นเท่านั้น สคริปต์นี้จำลองการทำงานนั้น นี่คือ "clear-logs.sh"
#!/bin/bash ประกาศ -a logfiles จำนวนไฟล์=0 rm_string="ก้อง" ฟังก์ชัน create_logfile() { ((++นับไฟล์)) filename=$(วันที่ +"%Y-%m-%d_%H-%M-%S") .log logfiles[$filecount]=$filename echo $filecount "สร้างแล้ว" ${logfiles[$filecount]} } # เนื้อหาของสคริปต์ การประมวลผลบางอย่างทำที่นี่ว่า # สร้างไฟล์บันทึกเป็นระยะ เราจะจำลองว่า create_logfile นอน3 create_logfile นอน3 create_logfile นอน3 create_logfile #มีไฟล์ใดบ้างที่จะลบ? สำหรับ ((file=1; file<=$filecount; file++)) ทำ # ลบไฟล์บันทึก eval $rm_string ${logfiles[$file]} "ถูกลบ..." ไฟล์บันทึก[$file]="" เสร็จแล้ว
สคริปต์ประกาศอาร์เรย์ที่เรียกว่า logfiles
ซึ่งจะเก็บชื่อของไฟล์บันทึกที่สร้างโดยสคริปต์ มันประกาศตัวแปรที่เรียกว่า filecount
ซึ่งจะเก็บจำนวนไฟล์บันทึกที่สร้างขึ้น
นอกจากนี้ยังประกาศสตริงที่เรียกว่า rm_string
ในสคริปต์ในโลกแห่งความเป็นจริง คำสั่งนี้จะมีคำสั่ง rm
แต่เรากำลังใช้ echo
เพื่อให้เราสามารถแสดงหลักการในลักษณะที่ไม่ทำลายล้าง
ฟังก์ชัน create_logfile()
คือตำแหน่งที่แต่ละล็อกไฟล์ถูกตั้งชื่อ และตำแหน่งที่จะเปิด เรากำลังสร้างเฉพาะ ชื่อไฟล์ และแสร้งทำเป็นว่ามันถูกสร้างขึ้นในระบบไฟล์
ฟังก์ชันเพิ่มตัวแปร filecount
ไฟล์ ค่าเริ่มต้นของมันคือศูนย์ ดังนั้นชื่อไฟล์แรกที่เราสร้างจะถูกเก็บไว้ที่ตำแหน่งหนึ่งในอาร์เรย์ สิ่งนี้ทำโดยเจตนาเช่นกันดูในภายหลัง
ชื่อไฟล์ถูกสร้างขึ้นโดยใช้คำสั่ง date
และนามสกุล ".log" ชื่อจะถูกเก็บไว้ในอาร์เรย์ที่ตำแหน่งที่ระบุโดย filecount
ชื่อจะถูกพิมพ์ไปที่หน้าต่างเทอร์มินัล ในสคริปต์ในโลกแห่งความเป็นจริง คุณจะต้องสร้างไฟล์จริงด้วย
เนื้อหาของสคริปต์ถูกจำลองโดยใช้คำสั่ง sleep
จะสร้างล็อกไฟล์แรก รอสามวินาที แล้วสร้างไฟล์อื่น มันสร้างไฟล์บันทึกสี่ไฟล์ โดยเว้นระยะห่างเพื่อให้การประทับเวลาในชื่อไฟล์ต่างกัน
ในที่สุดก็มีลูปที่ลบไฟล์บันทึก ไฟล์ตัวนับลูปถูกตั้งค่าเป็นหนึ่ง นับได้ถึงและรวมถึงค่าของ filecount
ซึ่งเก็บจำนวนไฟล์ที่สร้างขึ้น
หาก filecount
ยังคงถูกตั้งค่าเป็นศูนย์—เพราะไม่มีการสร้างไฟล์บันทึก—เนื้อความของลูปจะไม่ถูกดำเนินการเนื่องจากตัวหนึ่งมีค่าไม่น้อยกว่าหรือเท่ากับศูนย์ นั่นเป็นสาเหตุที่ตัวแปร filecount
ถูกตั้งค่าเป็นศูนย์เมื่อมีการประกาศและเหตุใดจึงเพิ่มขึ้น ก่อน ที่จะสร้างไฟล์แรก
ภายในลูป เราใช้ eval
กับ rm_string
แบบไม่ทำลายและชื่อไฟล์ที่ดึงมาจากอาร์เรย์ จากนั้นเราตั้งค่าองค์ประกอบอาร์เรย์เป็นสตริงว่าง
นี่คือสิ่งที่เราเห็นเมื่อเราเรียกใช้สคริปต์
./clear-logs.sh
มันไม่ได้แย่ไปซะหมด
eval
ที่ร้ายกาจมากมีประโยชน์อย่างแน่นอน เช่นเดียวกับเครื่องมือส่วนใหญ่ การใช้โดยประมาทถือเป็นอันตราย และมากกว่าหนึ่งวิธี
หากคุณแน่ใจว่าสตริงที่ใช้งานได้นั้นถูกสร้างขึ้นภายในและไม่ถูกดักจับจากมนุษย์, API หรือสิ่งต่างๆ เช่น คำขอ HTTPS คุณจะหลีกเลี่ยงข้อผิดพลาดที่สำคัญได้
ที่เกี่ยวข้อง: วิธีแสดงวันที่และเวลาใน Linux Terminal (และใช้ใน Bash Scripts)