ทำไม 0 จึงเป็นจริง แต่เท็จคือ 1 ในเชลล์


122
false; echo $?

ข้างต้นจะแสดงผลลัพธ์1ซึ่งขัดแย้งกับภาษาโปรแกรมอื่น ๆ ทั้งหมดที่ฉันรู้จัก

เหตุผลใดในเรื่องนี้


3
นอกจากนี้ยังสอดคล้องกับ Unix Way ... อย่าส่งคืนความสำเร็จ
Abdullah Jibaly

21
เนื่องจากสถานะการออกไม่ใช่บูลีน ง่ายๆแค่นั้นเอง
Jens

2
โปรดทราบว่าfalseไม่ใช่บูลีนเช่นเดียวกับภาษาโปรแกรมอื่น ๆ เป็นเพียงโปรแกรมที่อยู่ที่/bin/false( /usr/bin/falseบน Mac) ซึ่งมีไว้เพื่อส่งคืนรหัสทางออกข้อผิดพลาด 1 เสมอคล้ายกับtrue. ดังนั้นจึงไม่มีสิ่งที่เรียกว่าการคัดเลือกนักแสดงที่นี่ เป็นเพียงรหัสทางออกเท่านั้น
Mikhail Vasin

เชลล์คืออินเทอร์เฟซ (ผู้ใช้) กับระบบปฏิบัติการ โปรแกรม Unix เป็นต้นเป็นไปตามแบบแผนของการออกด้วย 0 เป็น OKด้วยเหตุผลที่ให้ไว้ในคำตอบ (ความสามารถในการสื่อสารเหตุผลความล้มเหลวมากกว่าหนึ่งข้อ) เปลือกยังคงรูปแบบนี้ไว้ซึ่งทำให้การสร้างเป็นเรื่องif myprog; then echo OK; fiง่ายและใช้งานง่าย มิฉะนั้นคุณจะต้องกลับการทดสอบทุกครั้งเพื่อความสำเร็จของโปรแกรม!
Peter - คืนสถานะ Monica

คำตอบ:


93

มันเป็นอนุสัญญา แต่มีประโยชน์อย่างยิ่งเมื่อคุณคิดถึงเรื่องนี้ โดยทั่วไปหากโปรแกรมประสบความสำเร็จนั่นคือทั้งหมดที่คุณต้องรู้ อย่างไรก็ตามหากล้มเหลวคุณอาจต้องรู้ข้อมูลทุกประเภทเกี่ยวกับความล้มเหลว - สาเหตุที่เกิดขึ้นวิธีแก้ไข ฯลฯ การมี 'ความสำเร็จ' เป็นศูนย์และความล้มเหลวเฉลี่ยที่ไม่ใช่ศูนย์ช่วยให้คุณสามารถตรวจสอบความสำเร็จได้อย่างง่ายดาย และตรวจสอบข้อผิดพลาดเฉพาะสำหรับรายละเอียดเพิ่มเติมหากคุณต้องการ API และเฟรมเวิร์กจำนวนมากมีหลักการที่คล้ายกัน - ฟังก์ชันที่ส่งคืนค่าเป็น 0 และฟังก์ชันที่ล้มเหลวจะให้รหัสข้อผิดพลาดที่อธิบายถึงกรณีความล้มเหลวโดยเฉพาะ


6
ฉันเข้าใจคำตอบนี้ แต่ยังไม่เข้าใจว่าเหตุใดการแปลง bool เป็น int จึงกลับหัว มี "return boolean convention" ที่ระบุว่า: ถ้าคุณส่งคืน true หมายความว่าไม่มีข้อผิดพลาดใด ๆ หากคุณส่งคืนเป็นเท็จหมายความว่ามีข้อผิดพลาดเกิดขึ้น (เช่น "integer = error code convention" ซึ่งเป็นที่รู้จักกันดีกว่า) หรือไม่
Guillaume86

1
สมมติว่ามีการแปลงบูลีนเป็น int หมายความว่าคุณไม่เข้าใจคำตอบจริงๆ ศูนย์หมายถึงความสำเร็จและ 1, 2, ... , 255 ทั้งหมดคือรหัสข้อผิดพลาดที่สามารถสื่อสารสถานการณ์ความล้มเหลวต่างๆได้อย่างมีประโยชน์ ตัวอย่างเฉพาะคือการxargsใช้รหัสข้อผิดพลาดที่แตกต่างกันในช่วงประมาณ 127 เพื่อระบุว่ากลุ่มคำสั่งล้มเหลวอย่างไร การแปลงที่สามารถทำได้คือ int to bool โดยที่ 0 แมปไปสู่ความสำเร็จ (ซึ่งฉันเดาว่าคุณต้องการแสดงเป็นจริง / 1 แต่ตระหนักดีว่านี่เป็นเพียงการประชุมตามอำเภอใจอื่น) และค่าอื่น ๆ ทั้งหมดที่เป็นความล้มเหลว
tripleee

79

Bash เป็นภาษาการเขียนโปรแกรม (สคริปต์) แต่ยังเป็นเชลล์และอินเทอร์เฟซผู้ใช้ด้วย หาก0เป็นข้อผิดพลาดโปรแกรมจะแสดงข้อผิดพลาดได้เพียงประเภทเดียว

อย่างไรก็ตามใน Bash ค่าที่ไม่ใช่ศูนย์เป็นข้อผิดพลาดและเราอาจใช้ตัวเลขใดก็ได้ตั้งแต่ 1-255 เพื่อแสดงข้อผิดพลาด ซึ่งหมายความว่าเราสามารถมีข้อผิดพลาดได้หลายประเภท 1เป็นข้อผิดพลาดทั่วไป126หมายความว่าไม่สามารถเรียกใช้ไฟล์ได้127หมายความว่า 'ไม่พบคำสั่ง' เป็นต้นนี่คือรายการรหัสทางออก Bash ที่มีความหมายพิเศษซึ่งแสดงรหัสทางออกที่พบบ่อยที่สุด

นอกจากนี้ยังมีความสำเร็จหลายประเภท (สถานะการออกคือ0) อย่างไรก็ตามความสำเร็จจะช่วยให้คุณดำเนินการในขั้นตอนต่อไปได้เช่นคุณสามารถพิมพ์ผลงานไปยังหน้าจอหรือดำเนินการคำสั่งเป็นต้น


8
คำตอบที่เป็นประโยชน์นี้แสดงให้เห็นถึงประโยชน์ของรหัสส่งคืนที่หลากหลายนั้นดีกว่าสำหรับการสั่งสอนของคำตอบอื่น ๆ
javadba

1
และเพื่อความสนุกฉันจะชี้ให้เห็นว่าการ/usr/include/sysexits.hบันทึกค่าการออกที่อาจจะเป็นแรงบันดาลใจมากกว่าแม้ว่าการประชุมจะแสดงย้อนหลังไปถึงปี 1980 ก็ตาม อนุสัญญานี้อยู่นอกขอบเขตของการทุบตี
ghoti

2
คำอธิบายที่ยอดเยี่ยมเรียบง่ายและเป็นประโยชน์ ต้องอยู่ด้านบนสุด
Rey Leonard Amorato

3
"ถ้า0เป็นข้อผิดพลาดแล้วโปรแกรมจะทำได้เพียงปัจจุบันหนึ่งชนิดของข้อผิดพลาด" คำสั่งนี้เป็นกุญแจสำคัญและเหตุผลที่คำตอบนี้ควรอยู่ด้านบน
rayryeng

1
@LuisLavaire นั่นคือพฤติกรรมที่ไม่ได้กำหนด ในทางปฏิบัติจำนวนมีแนวโน้มที่จะถูกตัดทอน แต่มันจะไม่ "ผิดไปมากกว่านี้" อย่างแน่นอนหากรันไทม์สร้างคำเตือนและ / หรือเพียงแค่ขัดข้อง ระบบปฏิบัติการสงวนไว้เพียงหนึ่งไบต์สำหรับข้อมูลนี้และการพยายามใส่มากกว่าหนึ่งไบต์มีข้อผิดพลาดแน่นอน แต่นักออกแบบระบบปฏิบัติการอาจประสบปัญหาในวันแรกและยักไหล่และตัดสินใจว่าสิ่งที่ดีที่สุดที่ทำได้คือเพียงแค่ตัดทอนค่าและดำเนินการต่อไป
tripleee

30

มีสองประเด็นที่เกี่ยวข้องที่นี่

อันดับแรกคำถามของ OP ทำไม 0 ถึงเป็นจริง แต่เท็จคือ 1 ในเชลล์? และประการที่สองเหตุใดแอปพลิเคชันจึงส่งคืน 0 สำหรับความสำเร็จและไม่ใช่ศูนย์สำหรับความล้มเหลว

ในการตอบคำถามของ OP เราจำเป็นต้องเข้าใจคำถามที่สอง คำตอบมากมายสำหรับโพสต์นี้ได้อธิบายว่านี่เป็นอนุสัญญาและได้ระบุรายละเอียดบางประการที่อนุสัญญานี้ให้ไว้ รายละเอียดบางส่วนเหล่านี้สรุปไว้ด้านล่าง

เหตุใดแอปพลิเคชันจึงคืนค่าเป็น 0 สำหรับความสำเร็จและไม่ใช่ศูนย์สำหรับความล้มเหลว

รหัสที่เรียกใช้การดำเนินการจำเป็นต้องรู้สองสิ่งเกี่ยวกับสถานะการออกของการดำเนินการ การดำเนินการออกสำเร็จหรือไม่ [* 1] และหากการดำเนินการออกไม่สำเร็จเหตุใดการดำเนินการจึงออกไม่สำเร็จ สามารถใช้ค่าใดก็ได้เพื่อแสดงถึงความสำเร็จ แต่ 0 สะดวกกว่าหมายเลขอื่น ๆ เนื่องจากเป็นแบบพกพาระหว่างแพลตฟอร์ม สรุปคำตอบของ xibo สำหรับคำถามนี้เมื่อวันที่ 16 ส.ค. 2554:

Zero ไม่ขึ้นอยู่กับการเข้ารหัส

หากเราต้องการจัดเก็บหนึ่ง (1) ในคำจำนวนเต็ม 32 บิตคำถามแรกจะเป็น "big-endian word หรือ little-endian word?" ตามด้วย "ไบต์ที่เขียนเป็นคำ endian ตัวน้อยยาวเท่าใด "ในขณะที่ศูนย์จะมีลักษณะเหมือนกันเสมอ

นอกจากนี้ยังต้องคาดหวังว่าบางคนจะร่าย errno ให้เป็นถ่านหรือสั้นในบางจุดหรือแม้กระทั่งลอย (int) ((ถ่าน) ENOLCK) ไม่ใช่ ENOLCK เมื่อถ่านมีความยาวอย่างน้อย 8 บิต (UNIX 7 บิตสนับสนุนเครื่องถ่าน ASCII) ในขณะที่ (int) ((ถ่าน) 0) เป็น 0 โดยไม่ขึ้นกับ รายละเอียดสถาปัตยกรรมของถ่าน

เมื่อพิจารณาแล้วว่า 0 จะเป็นค่าส่งกลับสำหรับความสำเร็จดังนั้นจึงควรใช้ค่าใด ๆ ที่ไม่ใช่ศูนย์สำหรับความล้มเหลว สิ่งนี้ทำให้รหัสทางออกจำนวนมากสามารถตอบคำถามว่าเหตุใดการดำเนินการจึงล้มเหลว

ทำไม 0 จึงเป็นจริง แต่เท็จคือ 1 ในเชลล์

หนึ่งในการใช้งานพื้นฐานของเชลล์คือการทำให้กระบวนการทำงานอัตโนมัติโดยการเขียนสคริปต์ โดยปกติจะหมายถึงการเรียกใช้การดำเนินการแล้วทำอย่างอื่นตามเงื่อนไขตามสถานะการออกของการดำเนินการ Philippe A.อธิบายอย่างดีในคำตอบของเขาสำหรับโพสต์นี้ว่า

ใน bash และในเปลือกหอย unix โดยทั่วไปค่าที่ส่งคืนไม่ใช่บูลีน เป็นรหัสทางออกจำนวนเต็ม

จำเป็นต้องตีความสถานะการออกของการดำเนินการเหล่านี้เป็นค่าบูลีน มันสมเหตุสมผลที่จะแมป0สถานะการออกสำเร็จ ( ) กับจริงและสถานะการออกที่ไม่ใช่ศูนย์ / ความล้มเหลวเป็นเท็จ การทำเช่นนี้ทำให้สามารถดำเนินการตามเงื่อนไขของคำสั่งเชลล์ที่ถูกล่ามโซ่

mkdir deleteme && cd $_ && pwdนี่คือตัวอย่าง เนื่องจากเชลล์แปลความหมายว่า 0 เป็นจริงคำสั่งนี้จึงทำงานได้อย่างสะดวกตามที่คาดไว้ หากเชลล์ตีความ 0 เป็นเท็จคุณจะต้องกลับสถานะทางออกที่ตีความสำหรับแต่ละการดำเนินการ

ในระยะสั้นมันจะเป็นเรื่องไร้สาระสำหรับเชลล์ที่จะตีความ 0 เป็นเท็จเนื่องจากข้อตกลงที่ว่าแอ็พพลิเคชันส่งคืน 0 สำหรับสถานะการออกที่สำเร็จ


[* 1]: ใช่หลายครั้งการดำเนินการจำเป็นต้องส่งคืนมากกว่าข้อความแสดงความสำเร็จธรรมดา ๆ แต่อยู่นอกเหนือขอบเขตของชุดข้อความนี้

โปรดดูภาคผนวก Eในคู่มือ Bash-Scripting ขั้นสูง


2
สวัสดีเพียงแค่เน้นว่าถ้าเชลล์ตีความศูนย์เป็นเท็จและไม่เป็นศูนย์ตามความเป็นจริงเคล็ดลับในการทำmkdir deleteme && cd _$ && pwdจะไม่ล้มเหลวจริงๆ แต่เราจะต้องแทนที่ด้วยmkdir deleteme || cd _$ || pwdซึ่งในความคิดของฉันยังไม่ค่อยชัดเจนนักเพราะสิ่งที่เราต้องการทำคือmkdir deletemeและcd _$และpwd... (โดย“ และ” มีความหมายจากภาษาธรรมดาในที่นี้)
Rémi Peyre

1
ฉันคิดว่าฉันครอบคลุมในคำตอบของฉัน เพื่อให้ชัดเจนว่าเมื่อคุณพลิกตรรกะมันไม่เพียงพอที่จะแทนที่ตัว&&ดำเนินการด้วย||ตัวดำเนินการ คุณจะต้องใช้กฎหมายของ De Morgan อย่างสมบูรณ์ ดู: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty

1
ข้อควรพิจารณาอีกประการหนึ่ง: ฉันเห็นเหตุผลอย่างน้อยสามประการว่าทำไมโดยทั่วไปแล้วมันเป็นเรื่องธรรมดาที่จะระบุว่าtrueสอดคล้องกับศูนย์และfalseสอดคล้องกับ nonzero อันดับแรกถ้าฉันบอกอะไรคุณ“ การบอกความจริงกับคุณ” ขึ้นอยู่กับจำนวนคำโกหกที่ฉันบอกไม่ว่าจะเป็นศูนย์และฉันได้บอกความจริงไปแล้ว (ทั่วโลก) หรือมันไม่ใช่ศูนย์และฉันก็โกหก โดยพื้นฐานแล้วสิ่งนี้ก็เหมือนกับการต้องการให้ตรรกะandสอดคล้องกับ "และ" ของการบวกทั่วไป (เมื่อ จำกัด เฉพาะจำนวนธรรมชาติอย่างเห็นได้ชัด)
Rémi Peyre

1
(ต่อจากความคิดเห็นก่อนหน้า) เหตุผลประการที่สองคือมีความจริงอย่างหนึ่ง แต่ไม่ใช่ความจริงมากมาย ดังนั้นจึงมีความสะดวกมากขึ้นในการเชื่อมโยงค่า แต่เพียงผู้เดียวtrueและค่าอื่น ๆ falseทั้งหมด (โดยพื้นฐานแล้วจะเหมือนกับการพิจารณาค่าตอบแทนของโปรแกรม)
Rémi Peyre

(ต่อจากความคิดเห็นก่อนหน้า) เหตุผลประการที่สามคือ "จริงที่ว่า A" เหมือนกับ "จริงที่ A" "เท็จว่าเท็จว่า A" เหมือนกับ "เท็จว่า A" เป็นต้น; เพื่อให้trueทำงานเป็นตัวคูณ 1 และfalseตัวคูณ -1 ซึ่งในฐานะที่เป็น -1 หมายความว่าtrueจะมีพฤติกรรมการบวกเป็น "คู่" และfalse"คี่" อีกครั้งtrueที่สมควรเป็นศูนย์ ...
Rémi Peyre

17

ประเด็นพื้นฐานประการหนึ่งที่ฉันพบว่าสำคัญที่จะต้องเข้าใจก็คือ ใน bash และในเปลือกหอย unix โดยทั่วไปค่าที่ส่งคืนไม่ใช่บูลีน เป็นรหัสทางออกจำนวนเต็ม ด้วยเหตุนี้คุณต้องประเมินค่าเหล่านี้ตามแบบแผนว่า 0 หมายถึงความสำเร็จและค่าอื่น ๆ หมายถึงข้อผิดพลาดบางอย่าง

ด้วยtest, [ ]หรือ[[ ]]ผู้ประกอบการเงื่อนไขทุบตีประเมินเป็นจริงในกรณีของรหัสทางออกของ 0 (ผลจากการ / ถัง / จริง) มิฉะนั้นจะประเมินว่าเป็นเท็จ

สตริงได้รับการประเมินแตกต่างจากรหัสทางออก:

if [ 0 ] ; then echo not null ; fi
if [ $(echo 0) ] ; then echo not null ; fi

if [ -z "" ] ; then echo null ; fi

ตัว(( ))ดำเนินการเลขคณิตตีความ 1 และ 0 ว่าเป็นจริงและเท็จ แต่ผู้ประกอบการที่ไม่สามารถนำมาใช้เป็นทดแทนที่สมบูรณ์สำหรับtest, หรือ[ ] [[ ]]นี่คือตัวอย่างที่แสดงเมื่อตัวดำเนินการเลขคณิตมีประโยชน์:

for (( counter = 0 ; counter < 10 ; counter ++ )) ; do
  if (( counter % 2 )) ; then echo "odd number $counter" ; fi
done

ขอบคุณสำหรับคำอธิบายของวงเล็บปีกกาและวงเล็บบนจริง / เท็จ
javadba

15

เป็นเพียงข้อตกลงว่ารหัสทางออก 0 หมายถึงความสำเร็จ EXIT_SUCCESSจะเป็น 0 ในเกือบทุกระบบที่ทันสมัย

แก้ไข:

"ทำไมทั้ง test 0 และ test 1 จึงส่งกลับ 0 (สำเร็จ)"

นั่นเป็นคำถามที่แตกต่างไปจากเดิมอย่างสิ้นเชิง คำตอบคือการส่งอาร์กิวเมนต์เดียวเพื่อทดสอบจะทำให้เกิดความสำเร็จเสมอเว้นแต่อาร์กิวเมนต์นั้นจะเป็นสตริงว่าง ("") ดูเอกสารเปิด Group


แล้วทำไมทั้งสองtest 0และtest 1คืนค่า 0 (สำเร็จ)?
httpinterpret

5
@httpinterpret testไม่ได้ทดสอบค่าตัวเลข จะทดสอบว่าสตริงนั้นเป็นสตริง null หรือไม่ man testสำหรับข้อมูลเพิ่มเติม.
Carl Norum

12

โดยทั่วไปโปรแกรมจะส่งคืนศูนย์สำหรับความสำเร็จไม่ใช่ศูนย์สำหรับความล้มเหลว falseส่งคืน 1 เนื่องจากเป็นค่าที่ไม่เป็นศูนย์ที่สะดวก แต่โดยทั่วไปค่าใด ๆ ที่ไม่ใช่ศูนย์หมายถึงความล้มเหลวของการจัดเรียงบางประเภทและหลายโปรแกรมจะส่งคืนค่าที่ไม่ใช่ศูนย์ที่แตกต่างกันเพื่อระบุโหมดความล้มเหลวที่แตกต่างกัน


2
ลงคะแนนโดยไม่มีคำอธิบาย (หรือเหตุผลที่ชัดเจน) ใด ๆ ก็ตามที่ห่วยและคนที่ทำมันเป็นง่อยเพราะพวกเขาไม่ได้ช่วยเหลือชุมชนเลย
Abdullah Jibaly

1
ไม่ยอมแพ้ ... แต่มันคือ 2 คะแนนของพวกเขา ... และ @MichaelMrozek .. ด้วย 19.6k มันไม่เจ็บเลยฮ่า ๆ
Alex Gray

1
@alexgray นี่คือสองปีที่แล้ว; ในเวลาที่ฉันมีประมาณ 5k. และฉันก็อยากรู้มากขึ้นว่ามีอะไรผิดปกติกับคำตอบมากกว่าตัวแทน
Michael Mrozek


4

เป็นการประชุมที่ย้อนกลับไปในยุคแรกของ Unix

ตามแบบแผนการเรียกระบบทั้งหมดจะคืนค่า 0 หากสำเร็จไม่ใช่ศูนย์เนื่องจากตัวเลขที่แตกต่างกันสามารถใช้เพื่อระบุสาเหตุของความล้มเหลวที่แตกต่างกันได้

เชลล์เป็นไปตามข้อตกลงนี้ 0 หมายถึงคำสั่งสุดท้ายที่ประสบความสำเร็จไม่ใช่ศูนย์ ในทำนองเดียวกันค่าที่ส่งคืนที่ไม่ใช่ศูนย์จะมีประโยชน์สำหรับข้อความแสดงข้อผิดพลาดที่ส่งออกเช่น 1: "brain dead", 2: "heartless" เป็นต้น


นี่คือคำตอบ
Peter - คืนสถานะ Monica

3

คุณพยายามที่จะถือเอาความจริง / เท็จกับความสำเร็จ / ความล้มเหลว

พวกเขาเป็นสองคนอย่างสมบูรณ์แม้ว่าในตอนแรกจะมีความแตกต่างกันอย่างละเอียด!

ในเชลล์สคริปต์ไม่มีสิ่งที่เรียกว่า true / false 'นิพจน์' ของเชลล์ไม่ถูกตีความว่าเป็นจริง / เท็จ แต่ 'นิพจน์' เชลล์เป็นกระบวนการที่ประสบความสำเร็จหรือล้มเหลว

เห็นได้ชัดว่ากระบวนการอาจล้มเหลวจากหลายสาเหตุ ดังนั้นเราจึงต้องใช้รหัสชุดที่ใหญ่กว่าเพื่อจับคู่ความล้มเหลวที่อาจเกิดขึ้น จำนวนเต็มบวกทำเคล็ดลับ ในทางกลับกันหากกระบวนการสำเร็จนั่นหมายความว่ามันทำในสิ่งที่ควรทำ เนื่องจากมีเพียงวิธีเดียวที่จะทำได้เราจึงต้องใช้รหัสเดียวเท่านั้น 0 เป็นเคล็ดลับ

ใน C เรากำลังสร้างโปรแกรม ในเชลล์สคริปต์เรากำลังเรียกใช้โปรแกรมมากมายเพื่อทำงานให้เสร็จ

ความแตกต่าง!


มันสับสน พิมพ์ "man [" มันแสดงให้เห็นว่ายูทิลิตี้การทดสอบประเมินนิพจน์และหากประเมินว่าเป็นจริงจะส่งคืนสถานะการออกเป็นศูนย์ (จริง) มิฉะนั้นจะส่งกลับ 1 (เท็จ) หากไม่มีนิพจน์การทดสอบจะส่งคืน 1 (เท็จ) ด้วย
cavalcade

1

วิธีที่ดีในการจำก็คือ:

  • รหัสส่งคืนตอบว่า "คำตอบคืออะไร" จริง (หรือผลลัพธ์อื่น ๆ ) หรือเท็จ
  • รหัสทางออกตอบว่า "มีปัญหาอะไร" รหัสออกหรือไม่มีปัญหา (0)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.