คำถามนี้ปรากฏในแบบทดสอบก่อนการสัมภาษณ์และมันทำให้ฉันบ้า ทุกคนสามารถตอบคำถามนี้และทำให้ฉันสบายใจได้ไหม? แบบทดสอบไม่มีการอ้างอิงถึงเชลล์เฉพาะ แต่รายละเอียดงานมีไว้สำหรับ unix sa คำถามก็คือ ...
'set -e' ทำอะไรและทำไมจึงอาจถือว่าเป็นอันตราย
คำถามนี้ปรากฏในแบบทดสอบก่อนการสัมภาษณ์และมันทำให้ฉันบ้า ทุกคนสามารถตอบคำถามนี้และทำให้ฉันสบายใจได้ไหม? แบบทดสอบไม่มีการอ้างอิงถึงเชลล์เฉพาะ แต่รายละเอียดงานมีไว้สำหรับ unix sa คำถามก็คือ ...
'set -e' ทำอะไรและทำไมจึงอาจถือว่าเป็นอันตราย
คำตอบ:
set -e
ทำให้เชลล์ออกหากคำสั่งย่อยหรือไพพ์ไลน์ส่งคืนสถานะที่ไม่ใช่ศูนย์
คำตอบที่ผู้สัมภาษณ์อาจมองหาคือ:
อาจเป็นอันตรายหากใช้ "set -e" เมื่อสร้างสคริปต์ init.d:
จากhttp://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -
ระวังการใช้ set -e ในสคริปต์ init.d การเขียนสคริปต์ init.d ที่ถูกต้องต้องยอมรับสถานะข้อผิดพลาดต่าง ๆ เมื่อ daemons กำลังทำงานอยู่หรือหยุดทำงานแล้วโดยไม่ยกเลิกสคริปต์ init.d และไลบรารีฟังก์ชัน init.d ทั่วไปไม่ปลอดภัยที่จะเรียกด้วย set -e สำหรับสคริปต์ init.d มักจะไม่ใช้ set -e และตรวจสอบผลลัพธ์ของแต่ละคำสั่งแยกกันแทน
นี่เป็นคำถามที่ถูกต้องจากมุมมองของผู้สัมภาษณ์เพราะเป็นการประเมินความรู้เกี่ยวกับการทำงานของสคริปต์ระดับเซิร์ฟเวอร์และระบบอัตโนมัติ
จากbash(1)
:
-e Exit immediately if a pipeline (which may consist of a
single simple command), a subshell command enclosed in
parentheses, or one of the commands executed as part of
a command list enclosed by braces (see SHELL GRAMMAR
above) exits with a non-zero status. The shell does not
exit if the command that fails is part of the command
list immediately following a while or until keyword,
part of the test following the if or elif reserved
words, part of any command executed in a && or ││ list
except the command following the final && or ││, any
command in a pipeline but the last, or if the command’s
return value is being inverted with !. A trap on ERR,
if set, is executed before the shell exits. This option
applies to the shell environment and each subshell envi-
ronment separately (see COMMAND EXECUTION ENVIRONMENT
above), and may cause subshells to exit before executing
all the commands in the subshell.
น่าเสียดายที่ฉันไม่ได้คิดสร้างสรรค์มากพอที่จะคิดว่าทำไมมันจะเป็นอันตรายนอกจาก "ส่วนที่เหลือของสคริปต์จะไม่ถูกเรียกใช้งาน" หรือ "อาจเป็นไปได้ว่าจะปกปิดปัญหาจริง"
set
! builtin(1)
ฉันมักจะจบลงที่ ขอบคุณ
set
คือการ befound ในman 1 bash
?? และใครจะหา-e
ตัวเลือกสำหรับset
คำหลักในหน้าคู่มือซึ่งมีขนาดใหญ่มากได้อย่างไร คุณไม่สามารถ/-e
ค้นหาได้ที่นี่
help set
ควรสังเกตว่าset -e
สามารถเปิดและปิดสำหรับส่วนต่างๆของสคริปต์ มันไม่จำเป็นที่จะต้องมีการดำเนินการของสคริปต์ทั้งหมด มันอาจเปิดใช้งานตามเงื่อนไข ที่กล่าวว่าฉันไม่เคยใช้มันตั้งแต่ฉันจัดการข้อผิดพลาดของตัวเอง (หรือไม่)
some code
set -e # same as set -o errexit
more code # exit on non-zero status
set +e # same as set +o errexit
more code # no exit on non-zero status
สิ่งที่น่าสังเกตก็คือในส่วนของ Bash man page ในtrap
คำสั่งซึ่งอธิบายวิธีการset -e
ทำงานภายใต้สถานการณ์บางอย่าง
แทร็บ ERR จะไม่ถูกดำเนินการหากคำสั่งที่ล้มเหลวเป็นส่วนหนึ่งของรายการคำสั่งทันทีหลังจากนั้นสักครู่หรือจนกว่าคำสำคัญส่วนหนึ่งของการทดสอบในคำสั่ง if ส่วนหนึ่งของคำสั่งที่ดำเนินการในรายการ && หรือ⎪⎪หรือถ้าคำสั่ง ค่าที่ส่งคืนจะถูกคว่ำผ่าน! เงื่อนไขเหล่านี้เป็นไปตามเงื่อนไขของตัวเลือก errexit
ดังนั้นจึงมีเงื่อนไขบางประการที่สถานะที่ไม่ใช่ศูนย์จะไม่ทำให้เกิดการออก
ฉันคิดว่าอันตรายไม่เข้าใจเมื่อset -e
เข้ามาเล่นและเมื่อมันไม่ได้และพึ่งพามันอย่างไม่ถูกต้องภายใต้สมมติฐานที่ไม่ถูกต้อง
โปรดดูBashFAQ / 105 เหตุใดจึงไม่ตั้งค่า -e (หรือตั้งค่า -o errexit หรือ trap ERR) ทำตามที่ฉันคาดไว้
โปรดทราบว่านี่เป็นคำถามสำหรับการสัมภาษณ์งาน อาจมีคำถามที่เขียนโดยเจ้าหน้าที่ปัจจุบันและพวกเขาอาจจะผิด สิ่งนี้ไม่ได้เลวร้ายนักและทุกคนทำผิดพลาดและคำถามสัมภาษณ์มักจะนั่งในมุมมืดโดยไม่ต้องทบทวนและจะออกมาในระหว่างการสัมภาษณ์เท่านั้น
เป็นไปได้ทั้งหมดที่ 'set -e' ไม่ทำอะไรเลยที่เราจะพิจารณาว่า "อันตราย" แต่ผู้เขียนคำถามนั้นอาจเชื่อผิดว่า 'set -e' เป็นอันตรายเนื่องจากความไม่รู้หรืออคติของพวกเขาเอง บางทีพวกเขาอาจเขียนสคริปต์รถบั๊กกี้ แต่มันก็ระเบิดอย่างน่ากลัวและพวกเขาคิดผิดว่า 'set -e' ผิดพลาดจริง ๆ แล้วพวกเขาละเลยที่จะเขียนการตรวจสอบข้อผิดพลาดที่เหมาะสม
ฉันเคยเข้าร่วมสัมภาษณ์งาน 40 ครั้งในช่วง 2 ปีที่ผ่านมาและบางครั้งผู้สัมภาษณ์ถามคำถามที่ผิดหรือมีคำตอบที่ผิด
หรือบางทีมันอาจเป็นคำถามที่หลอกลวงซึ่งอาจจะง่อย แต่ก็ไม่ได้คาดไม่ถึงเลย
หรือนี่อาจเป็นคำอธิบายที่ดี: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html
set -e
บอก bash ในสคริปต์ให้ออกเมื่อใดก็ตามที่ส่งคืนค่าส่งคืนที่ไม่เป็นศูนย์
ฉันสามารถดูว่ามันจะน่ารำคาญและบั๊กกี้ไม่แน่ใจเกี่ยวกับอันตรายเว้นแต่ว่าคุณได้เปิดการอนุญาตในบางสิ่งและก่อนที่คุณจะสามารถ จำกัด ได้อีกสคริปต์ของคุณก็ตาย
set -e
ในความเป็นจริงสำหรับฉันมันพูดถึงความเกียจคร้านที่จะละเว้นการตรวจสอบข้อผิดพลาดในสคริปต์ของคุณ ... ดังนั้นโปรดจำไว้ว่ากับนายจ้างคนนี้เช่นกัน ... ถ้าพวกเขาใช้มัน จำนวนมากสิ่งที่สคริปต์ของพวกเขามีลักษณะเหมือนที่คุณย่อมจะมีการรักษา ...
set -e
ยุติสคริปต์หากพบรหัสทางออกที่ไม่ใช่ศูนย์ยกเว้นภายใต้เงื่อนไขบางประการ เพื่อสรุปอันตรายของการใช้งานในไม่กี่คำ: มันไม่ได้ทำงานอย่างที่ผู้คนคิด
ในความคิดของฉันมันควรจะถือว่าเป็นแฮ็คที่อันตรายซึ่งยังคงมีอยู่เพื่อจุดประสงค์ในการทำงานร่วมกันเท่านั้น set -e
คำสั่งไม่ได้เปิดเปลือกจากภาษาที่ใช้รหัสข้อผิดพลาดเป็นภาษาที่ใช้ในการควบคุมการไหลยกเว้นเหมือนมันไม่ดีเพียงแค่พยายามที่จะเลียนแบบพฤติกรรมที่
Greg Wooledge มีหลายสิ่งที่ควรพูดเกี่ยวกับอันตรายของset -e
:
set -e
)ในการเชื่อมโยงที่สองมีตัวอย่างต่างๆของพฤติกรรม unintuitive set -e
และคาดเดาไม่ได้ของ
ตัวอย่างบางส่วนของพฤติกรรมที่ไม่เข้าใจง่ายของset -e
(บางคนนำมาจากลิงก์วิกิข้างต้น):
ชุด -e x = 0 ให้ x ++ echo "x คือ $ x"ด้านบนจะทำให้เชลล์สคริปต์ออกก่อนกำหนดเนื่องจาก
let x++
ส่งคืน 0 ซึ่งถือว่าโดยlet
คำหลักเป็นค่าที่ผิดพลาดและเปลี่ยนเป็นรหัสออกที่ไม่ใช่ศูนย์ set -e
สังเกตสิ่งนี้และยุติสคริปต์แบบเงียบ ๆชุด -e [-d / opt / foo] && echo "คำเตือน: ติดตั้ง foo แล้วจะเขียนทับ" > & 2 echo "กำลังติดตั้ง foo ... "
งานด้านบนตามที่คาดไว้พิมพ์คำเตือนหาก/opt/foo
มีอยู่แล้ว
ชุด -e check_previous_install () { [-d / opt / foo] && echo "คำเตือน: ติดตั้ง foo แล้วจะเขียนทับ" > & 2 } check_previous_install echo "กำลังติดตั้ง foo ... "
ด้านบนแม้จะมีความแตกต่างเพียงอย่างเดียวที่บรรทัดเดียวได้รับการปรับโครงสร้างให้เป็นฟังก์ชัน แต่จะยุติหาก/opt/foo
ไม่มีอยู่ เนื่องจากความจริงที่ว่ามันทำงานได้ แต่เดิมนั้นเป็นข้อยกเว้นพิเศษสำหรับset -e
พฤติกรรมของ เมื่อผลตอบแทนที่ไม่ใช่ศูนย์ก็จะถูกละเลยโดยa && b
set -e
อย่างไรก็ตามตอนนี้มันเป็นฟังก์ชั่นรหัสออกของฟังก์ชั่นจะเท่ากับรหัสทางออกของคำสั่งนั้นและฟังก์ชั่นที่ส่งกลับไม่ใช่ศูนย์จะยุติสคริปต์อย่างเงียบ ๆ
ชุด -e IFS = $ '\ n' read -d '' -r -a config_vars <config
ดังกล่าวข้างต้นจะอ่านอาร์เรย์จากแฟ้มconfig_vars
config
ในฐานะผู้เขียนอาจตั้งใจมันจะจบลงด้วยข้อผิดพลาดหากconfig
หายไป เนื่องจากผู้เขียนอาจไม่ได้ตั้งใจมันจะเงียบหากconfig
ไม่ได้ขึ้นบรรทัดใหม่ หากset -e
ไม่ได้ใช้ที่นี่config_vars
จะมีทุกบรรทัดของไฟล์ไม่ว่าจะสิ้นสุดในการขึ้นบรรทัดใหม่หรือไม่
ผู้ใช้ Sublime Text (และโปรแกรมแก้ไขข้อความอื่นที่จัดการบรรทัดใหม่ไม่ถูกต้อง) ระวัง
ชุด -e should_audit_user () { กลุ่มโลคัล = "$ (กลุ่ม" $ 1 ")" สำหรับกลุ่มในกลุ่ม $; ทำ หาก ["$ group" = audit]; จากนั้นส่งคืน 0 Fi เสร็จแล้ว คืน 1 } ถ้า should_audit_user "$ user"; แล้วก็ คนตัดไม้ 'Blah' Fi
ผู้เขียนที่นี่อาจคาดหวังอย่างสมเหตุสมผลว่าหากมีเหตุผลบางอย่างที่ผู้ใช้$user
ไม่มีอยู่groups
คำสั่งจะล้มเหลวและสคริปต์จะยุติลงแทนที่จะปล่อยให้ผู้ใช้ดำเนินการบางอย่างโดยไม่มีการตรวจสอบ อย่างไรก็ตามในกรณีนี้การset -e
ยกเลิกจะไม่มีผล หาก$user
ไม่พบด้วยเหตุผลบางอย่างแทนการยกเลิกสคริปต์should_audit_user
ฟังก์ชันจะส่งคืนข้อมูลที่ไม่ถูกต้องราวกับว่าset -e
ไม่มีผลบังคับใช้
นี้นำไปใช้ฟังก์ชั่นใด ๆ เรียกจากส่วนเงื่อนไขของif
คำสั่งไม่ว่าซ้อนกันลึกเรื่องที่มันถูกกำหนดไว้ไม่ว่าแม้ว่าคุณจะไม่ทำงานset -e
ภายในอีกครั้ง การใช้if
ณ จุดใด ๆ จะเป็นการปิดการใช้งานเอฟเฟกต์ของset -e
จนกว่าเงื่อนไขบล็อกจะถูกดำเนินการอย่างสมบูรณ์ หากผู้เขียนไม่ได้ตระหนักถึงอันตรายนี้หรือไม่ทราบว่าเรียกกองของพวกเขาทั้งในสถานการณ์ที่เป็นไปได้ทั้งหมดซึ่งเป็นฟังก์ชั่นสามารถเรียกว่าแล้วพวกเขาก็จะเขียนโค้ดรถและความผิดพลาดของการรักษาความปลอดภัยให้โดยset -e
จะมีอย่างน้อยบางส่วน ตำหนิ.
แม้ว่าผู้เขียนจะตระหนักถึงข้อผิดพลาดนี้อย่างเต็มที่วิธีแก้ไขคือการเขียนโค้ดในลักษณะเดียวกับที่ผู้เขียนจะเขียนโดยไม่มีset -e
การเรนเดอร์ที่มีประสิทธิภาพน้อยกว่าไร้ประโยชน์ ผู้เขียนไม่เพียง แต่ต้องเขียนรหัสการจัดการข้อผิดพลาดด้วยตนเองราวกับset -e
ว่าไม่ได้มีผล แต่การปรากฏตัวของset -e
อาจหลอกพวกเขาคิดว่าพวกเขาไม่จำเป็นต้อง
ข้อเสียเพิ่มเติมของset -e
:
let x++
ข้างต้นนี่ไม่ใช่กรณี หากสคริปต์ตายโดยไม่คาดคิดมักจะเงียบซึ่งเป็นอุปสรรคต่อการดีบัก หากสคริปต์ไม่ตายและคุณคาดหวังให้ (ดูหัวข้อย่อยก่อนหน้านี้) แสดงว่าคุณมีข้อบกพร่องที่บอบบางและร้ายกาจมากกว่าในมือของคุณif
จุดกระสุน -condition อีกครั้งset -e
การถูกปรับแต่งระหว่างเวอร์ชั่นเหล่านั้นset -e
เป็นปัญหาที่ถกเถียงกันและบางคนตระหนักถึงปัญหาที่อยู่รอบ ๆ มันแนะนำในขณะที่บางคนก็แนะนำให้ดูแลในขณะที่มันมีการใช้งานเพื่อทราบข้อผิดพลาด มีผู้เริ่มต้นเชลล์สคริปต์จำนวนมากที่แนะนำให้ใช้set -e
กับสคริปต์ทั้งหมดว่าเป็นข้อผิดพลาดทั้งหมด แต่ในชีวิตจริงมันใช้งานไม่ได้
set -e
ไม่ทดแทนการศึกษา
ฉันจะบอกว่ามันอันตรายเพราะคุณไม่สามารถควบคุมการไหลของสคริปต์ของคุณได้อีกต่อไป สคริปต์สามารถยุติตราบใดที่คำสั่งใด ๆ ที่เรียกใช้สคริปต์ส่งคืนค่าที่ไม่เป็นศูนย์ ดังนั้นสิ่งที่คุณต้องทำคือทำสิ่งที่เปลี่ยนแปลงพฤติกรรมหรือผลลัพธ์ขององค์ประกอบใด ๆ และคุณจะต้องยุติสคริปต์หลัก มันอาจเป็นปัญหาของสไตล์มากกว่า แต่ก็มีผลกระทบแน่นอน จะเป็นอย่างไรถ้าสคริปต์หลักของคุณควรตั้งค่าสถานะบางอย่างและไม่ได้เพราะสิ้นสุดลงก่อนเวลา คุณจะได้ข้อผิดพลาดส่วนที่เหลือของระบบถ้ามันถือว่าธงควรจะมีหรือทำงานกับค่าเริ่มต้นหรือค่าเก่าที่ไม่คาดคิด
เป็นตัวอย่างที่เป็นรูปธรรมมากขึ้นสำหรับคำตอบของ @ Marcin ที่กัดฉันเป็นการส่วนตัวลองจินตนาการว่ามีrm foo/bar.txt
บรรทัดหนึ่งในสคริปต์ของคุณ มักจะไม่มีเรื่องใหญ่หากfoo/bar.txt
ไม่มีอยู่จริง อย่างไรก็ตามด้วยset -e
ปัจจุบันสคริปต์ของคุณจะสิ้นสุดก่อนเวลานั้น! อุ่ย