คำถามนี้ปรากฏในแบบทดสอบก่อนการสัมภาษณ์และมันทำให้ฉันบ้า ทุกคนสามารถตอบคำถามนี้และทำให้ฉันสบายใจได้ไหม? แบบทดสอบไม่มีการอ้างอิงถึงเชลล์เฉพาะ แต่รายละเอียดงานมีไว้สำหรับ 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ปัจจุบันสคริปต์ของคุณจะสิ้นสุดก่อนเวลานั้น! อุ่ย