Tại sao 0 là true nhưng false là 1 trong shell?


122
false; echo $?

Ở trên sẽ xuất ra 1, điều này mâu thuẫn với tất cả các ngôn ngữ lập trình khác mà tôi biết.

Bất kỳ lý do trong điều này?


3
Nó cũng phù hợp với Unix Way ... không trả lại bất cứ điều gì cho thành công.
Abdullah Jibaly

21
Bởi vì trạng thái thoát không phải là boolean. Đơn giản như vậy.
Jens

2
Hãy nhớ rằng đó falsekhông phải là boolean, như trong các ngôn ngữ lập trình khác. Nó chỉ là một chương trình đặt tại /bin/false( /usr/bin/falsetrên máy Mac) có nghĩa là luôn trả về mã thoát lỗi 1. Tương tự cho true. Vì vậy, không có cái gọi là đúc ở đây. Đó chỉ là tất cả về mã thoát.
Mikhail Vasin

Shell là một giao diện (người dùng) với một hệ điều hành. Các chương trình Unix, v.v. tuân theo quy ước thoát bằng 0 là OK, vì lý do được đưa ra trong câu trả lời (khả năng giao tiếp nhiều hơn một lý do lỗi). Shell chỉ đơn giản là giữ quy ước này, điều này làm cho các cấu trúc như if myprog; then echo OK; fidễ dàng và trực quan. Nếu không, bạn sẽ phải đảo ngược mọi thử nghiệm để chương trình thành công!
Peter - Phục hồi Monica

Câu trả lời:


93

Đó là một quy ước, nhưng là một quy ước đặc biệt hữu ích khi bạn nghĩ về nó. Nói chung, nếu một chương trình thành công thì đó là tất cả những gì bạn cần biết. Tuy nhiên, nếu nó không thành công, bạn có thể cần biết tất cả các loại thông tin về thất bại - tại sao nó xảy ra, cách khắc phục nó, v.v. Không có nghĩa là 'thành công' và thất bại có nghĩa là khác 0 cho phép bạn có thể kiểm tra thành công khá dễ dàng và điều tra lỗi cụ thể để biết thêm chi tiết nếu bạn muốn. Rất nhiều API và khung công tác có quy ước tương tự - các hàm thành công trả về 0 và các hàm không thành công trả về mã lỗi mô tả trường hợp lỗi cụ thể.


6
Tôi hiểu câu trả lời này, nhưng tôi vẫn không hiểu tại sao chuyển đổi bool sang int bị đảo ngược. Có một "quy ước boolean trả về" nói rằng: nếu bạn trả về true, nghĩa là không có lỗi, nếu bạn trả về false, có nghĩa là đã xảy ra lỗi (như "quy ước mã số nguyên = lỗi" được biết đến nhiều hơn)?
Guillaume86

1
Giả sử có một chuyển đổi boolean thành int có nghĩa là bạn không thực sự hiểu câu trả lời. Số không có nghĩa là thành công và tất cả 1, 2, ..., 255 là mã lỗi có thể giao tiếp hữu ích các tình huống thất bại khác nhau. Một ví dụ cụ thể là xargssử dụng các mã lỗi khác nhau trong phạm vi khoảng 127 để chỉ ra cách một nhóm lệnh bị lỗi. Việc chuyển đổi có thể được thực hiện là int thành bool trong đó 0 ánh xạ đến thành công (mà tôi đoán bạn muốn diễn đạt là true / 1; nhưng nhận ra rằng đây chỉ là một quy ước tùy ý khác) và tất cả các giá trị khác đều không thành công.
tripleee

79

Bash là một ngôn ngữ lập trình (script), nhưng nó cũng là một shell và một giao diện người dùng. Nếu 0là lỗi, thì chương trình chỉ có thể đưa ra một loại lỗi.

Tuy nhiên trong Bash, mọi giá trị khác không đều là lỗi và chúng tôi có thể sử dụng bất kỳ số nào từ 1-255 để biểu thị lỗi. Điều này có nghĩa là chúng ta có thể mắc nhiều loại lỗi khác nhau. 1là một lỗi chung, 126nghĩa là không thể thực thi tệp, 127có nghĩa là 'không tìm thấy lệnh', v.v. Dưới đây là danh sách Mã thoát Bash có ý nghĩa đặc biệt hiển thị một số mã thoát phổ biến nhất.

Cũng có nhiều loại thành công (trạng thái thoát là 0). Tuy nhiên, thành công sẽ cho phép bạn tiếp tục bước tiếp theo — bạn có thể in kết quả ra màn hình hoặc thực hiện một lệnh, v.v.


8
Câu trả lời thực dụng này cho thấy tính hữu ích của nhiều loại mã trả lại thích hợp hơn so với lời rao giảng của một số câu trả lời khác.
javadba

1
Và chỉ để nói cho vui thôi, tôi sẽ chỉ ra rằng /usr/include/sysexits.hlưu ý các giá trị thoát có lẽ mang tính tham vọng hơn, mặc dù quy ước mà chúng thể hiện có từ những năm 1980. Quy ước này tồn tại bên ngoài phạm vi bash.
ghoti

2
Một lời giải thích tuyệt vời, đơn giản và thực dụng. Điều này cần phải được đặt lên hàng đầu.
Rey Leonard Amorato

3
"Nếu 0là lỗi, thì chương trình chỉ có thể đưa ra một loại lỗi" . Tuyên bố này là chìa khóa và lý do tại sao câu trả lời này nên ở trên cùng.
rayryeng

1
@LuisLavaire Đó là hành vi không xác định. Trong thực tế, con số có xu hướng bị cắt bớt; nhưng chắc chắn sẽ không "sai" hơn nếu thời gian chạy tạo ra cảnh báo và / hoặc đơn giản là bị lỗi. Hệ điều hành dành chính xác một byte cho thông tin này và cố gắng đặt nhiều hơn một byte chắc chắn có lỗi. Nhưng các nhà thiết kế hệ điều hành có lẽ đã gặp sự cố vào ngày đầu tiên và nhún vai, và quyết định rằng điều tốt nhất họ có thể làm là chỉ cắt bớt giá trị và tiếp tục.
tripleee

30

Có hai vấn đề liên quan ở đây.

Đầu tiên, câu hỏi của OP, Tại sao 0 là đúng nhưng sai là 1 trong shell? và thứ hai, tại sao các ứng dụng trả về 0 cho thành công và khác 0 cho thất bại?

Để trả lời câu hỏi của OP, chúng ta cần hiểu câu hỏi thứ hai. Nhiều câu trả lời cho bài đăng này đã mô tả rằng đây là một quy ước và đã liệt kê một số điều tốt đẹp mà quy ước này mang lại. Một số điều tốt đẹp này được tóm tắt dưới đây.

Tại sao các ứng dụng trả về 0 cho thành công và khác 0 cho thất bại?

Mã gọi một hoạt động cần biết hai điều về trạng thái thoát của hoạt động. Đã thoát hoạt động thành công? [* 1] Và nếu hoạt động không thoát thành công tại sao hoạt động thoát không thành công? Bất kỳ giá trị nào cũng có thể được sử dụng để biểu thị thành công. Nhưng số 0 thuận tiện hơn bất kỳ số nào khác vì nó có thể di động giữa các nền tảng. Tóm tắt câu trả lời của xibo cho câu hỏi này vào ngày 16 tháng 8 năm 2011:

Zero không phụ thuộc vào mã hóa.

Nếu chúng ta muốn lưu trữ một (1) trong một từ số nguyên 32 bit, câu hỏi đầu tiên sẽ là "từ big-endian hay little-endian từ?", Tiếp theo là "bao lâu thì các byte tạo ra một từ little-endian? ", trong khi số 0 sẽ luôn giống nhau.

Ngoài ra, cần phải mong đợi rằng một số người biến lỗi thành char hoặc short tại một số thời điểm, hoặc thậm chí là nổi. (int) ((char) ENOLCK) không phải là ENOLCK khi ký tự không dài ít nhất 8 bit (các máy ký tự ASCII 7 bit được UNIX hỗ trợ), trong khi (int) ((char) 0) là 0 độc lập với chi tiết kiến ​​trúc của char.

Khi đã xác định được rằng 0 sẽ là giá trị trả về cho sự thành công, thì việc sử dụng bất kỳ giá trị nào khác 0 cho sự thất bại là rất hợp lý. Điều này cho phép nhiều mã thoát để trả lời câu hỏi tại sao hoạt động không thành công.

Tại sao 0 là true nhưng false là 1 trong shell?

Một trong những cách sử dụng cơ bản của shell là tự động hóa các quy trình bằng cách viết kịch bản cho chúng. Thông thường, điều này có nghĩa là gọi một hoạt động và sau đó thực hiện điều gì đó khác có điều kiện dựa trên trạng thái thoát của hoạt động. Philippe A. đã giải thích một cách độc đáo trong câu trả lời của mình cho bài đăng này rằng

Trong bash và trong unix shell nói chung, giá trị trả về không phải là boolean. Chúng là các mã thoát số nguyên.

Sau đó, điều cần thiết là phải diễn giải trạng thái thoát của các hoạt động này dưới dạng giá trị boolean. Có ý nghĩa khi ánh xạ 0trạng thái thoát thành công ( ) thành true và bất kỳ trạng thái thoát nào khác 0 / thất bại thành false. Làm điều này cho phép thực hiện có điều kiện các lệnh shell chuỗi.

Đây là một ví dụ mkdir deleteme && cd $_ && pwd. Bởi vì shell diễn giải 0 là true, lệnh này hoạt động thuận tiện như mong đợi. Nếu shell diễn giải 0 là false thì bạn phải đảo ngược trạng thái thoát đã diễn giải cho mỗi thao tác.

Nói tóm lại, sẽ vô nghĩa nếu shell diễn giải 0 là false theo quy ước rằng các ứng dụng trả về 0 cho trạng thái thoát thành công.


[* 1]: Có, nhiều khi các phép toán cần trả lại nhiều hơn một thông báo thành công đơn giản nhưng điều đó nằm ngoài phạm vi của chuỗi này.

Xem thêm Phụ lục E trong Hướng dẫn Viết mã Bash Nâng cao


2
Xin chào, Chỉ cần nhấn mạnh rằng, nếu shell diễn giải số 0 là sai và khác 0 là đúng, thì thủ thuật thực hiện mkdir deleteme && cd _$ && pwdsẽ không thực sự thất bại; nhưng chúng tôi sẽ phải thay thế nó bằng mkdir deleteme || cd _$ || pwd, theo ý kiến ​​của tôi là không rõ ràng hơn nhiều, bởi vì những gì chúng tôi thực sự muốn làm là mkdir deletemecd _$pwd... (với “và” ở đây có nghĩa là từ ngôn ngữ thông thường).
Rémi Peyre

1
Tôi nghĩ rằng tôi đã đề cập đến điều đó trong câu trả lời của mình. Tuy nhiên, để rõ ràng hơn, khi bạn đảo ngược logic thì không đủ nếu chỉ thay thế các &&toán tử bằng các ||toán tử. Bạn cần phải áp dụng đầy đủ định luật De Morgan. Xem: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty

1
Một cân nhắc khác: Tôi thấy ít nhất ba lý do tại sao, trên cơ sở chung, sẽ là lẽ tự nhiên khi nói rằng truetương ứng với 0 và falsetương ứng với khác không. Đầu tiên, nếu tôi nói với bạn điều gì đó, "việc đã nói với bạn sự thật" phụ thuộc vào số lượng lời nói dối mà tôi đã nói: hoặc nó bằng 0 và tôi đã nói (trên toàn cầu) sự thật, hoặc nó không phải là sự thật và tôi đã nói dối. Về cơ bản, điều này giống với việc muốn lôgic andtương ứng với “và” chung của phép cộng (rõ ràng là khi giới hạn ở các số tự nhiên).
Rémi Peyre

1
(tiếp theo bình luận trước). Lý do thứ hai là có một sự thật nhưng nhiều sự thật không phải là sự thật; vì vậy, sẽ thuận tiện hơn khi liên kết một giá trị duy nhất cho truevà tất cả các giá trị khác cho false. (Điều này về cơ bản giống như việc xem xét giá trị trả về của các chương trình).
Rémi Peyre

(tiếp theo bình luận trước). Lý do thứ ba là “đúng là đúng khi A” giống với “đúng là A”, “sai mà sai rằng A” giống với “sai rằng A”, v.v.; để nó truehoạt động như một phép nhân 1 và falsenhư một phép nhân -1. Mà, như lũy thừa của -1, có nghĩa là truehành vi phân tích cộng là "chẵn" và false"lẻ". Một lần nữa, nó truexứng đáng là con số không ...
Rémi Peyre

17

Một điểm cơ bản mà tôi thấy quan trọng cần hiểu là điều này. Trong bash và trong unix shell nói chung, giá trị trả về không phải là boolean. Chúng là các mã thoát số nguyên. Do đó, bạn phải đánh giá chúng theo quy ước nói rằng 0 nghĩa là thành công và các giá trị khác có nghĩa là một số lỗi.

Với test, [ ]hoặc các [[ ]]toán tử, các điều kiện cơ sở đánh giá là đúng trong trường hợp mã thoát là 0 (kết quả của / bin / true). Nếu không, họ đánh giá là sai.

Các chuỗi được đánh giá khác với mã thoát:

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

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

Các (( ))toán tử số học giải thích 1 và 0 là đúng và sai. Nhưng điều hành mà không thể được sử dụng như một sự thay thế hoàn toàn cho test, [ ]hoặc [[ ]]. Dưới đây là một ví dụ cho thấy khi nào toán tử số học hữu ích:

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

Cảm ơn vì lời giải thích về dấu ngoặc nhọn và dấu ngoặc đơn trên true / false
javadba

15

Nó chỉ là một quy ước rằng một mã thoát 0 có nghĩa là thành công. EXIT_SUCCESS sẽ là 0 trên hầu hết mọi hệ thống hiện đại.

BIÊN TẬP:

"tại sao cả thử nghiệm 0 và thử nghiệm 1 đều trả về 0 (thành công)?"

Đó là một câu hỏi hoàn toàn khác. Câu trả lời là việc chuyển một đối số duy nhất để kiểm tra luôn dẫn đến thành công trừ khi đối số đó là chuỗi null (""). Xem tài liệu Nhóm Mở .


Sau đó, tại sao cả hai test 0test 1trả về 0 (thành công)?
httpinterpret

5
@httpinterpret, testkhông kiểm tra các giá trị số. Nó kiểm tra xem chuỗi đó có phải là chuỗi rỗng hay không. man testđể biết thêm thông tin.
Carl Norum

12

Thông thường, các chương trình trả về 0 cho thành công, khác 0 cho thất bại; falsetrả về 1 vì đó là một giá trị khác 0 thuận tiện, nhưng nói chung mọi giá trị khác 0 đều có nghĩa là không thành công trong một số loại và nhiều chương trình sẽ trả về các giá trị khác 0 để chỉ ra các chế độ lỗi khác


2
Phản đối mà không có lời giải thích (hoặc lý do rõ ràng) bất kỳ điều gì hấp dẫn và những người làm điều đó là khập khiễng vì họ chẳng giúp ích gì cho cộng đồng cả.
Abdullah Jibaly

1
Không chê vào đâu được… nhưng đó 2 điểm của họ… và @MichaelMrozek .. với 19,6k, cũng không tệ lắm, lol.
Alex Gray,

1
@alexgray Đây là hai năm trước; vào thời điểm đó tôi có khoảng 5k. Và tôi đã tò mò hơn những gì đã sai với câu trả lời hơn về đại diện
Michael Mrozek


4

đó là một quy ước có từ những ngày đầu của Unix.

Theo quy ước, tất cả các lệnh gọi hệ thống trả về 0 nếu thành công, khác 0 nếu thành công, vì sau đó các số khác nhau có thể được sử dụng để chỉ ra lý do thất bại khác nhau.

Vỏ tuân theo quy ước này, 0 có nghĩa là lệnh cuối cùng đã thành công, ngược lại là khác 0. Tương tự, giá trị trả về khác 0 có ích cho các thông báo lỗi đầu ra: ví dụ: 1: "brain dead", 2: "heartless", v.v.


Đây là câu trả lời.
Peter - Phục hồi Monica

3

Việc bạn cố gắng đánh đồng true / false với thành công / thất bại.

Chúng là hai hoàn toàn, mặc dù thoạt nghe, phân đôi khác nhau một cách tinh vi!

Trong kịch bản shell, không có cái gọi là true / false. Các 'biểu thức' shell không được hiểu là true / false. Thay vào đó, 'biểu thức' của trình bao là các quy trình thành công hoặc thất bại.

Rõ ràng, một quá trình có thể thất bại vì nhiều lý do. Vì vậy, chúng tôi cần một bộ mã lớn hơn để ánh xạ các lỗi có thể xảy ra. Các số nguyên dương thực hiện thủ thuật. Mặt khác, nếu quá trình thành công, điều đó có nghĩa là nó đã làm chính xác những gì nó được cho là phải làm. Vì chỉ có một cách để làm điều đó, chúng tôi chỉ cần một mã. 0 thực hiện thủ thuật.

Trong C, chúng tôi đang tạo một chương trình. Trong một kịch bản shell, chúng ta đang chạy một loạt các chương trình để hoàn thành một việc gì đó.

Sự khác biệt!


Thật khó hiểu. Nhập 'man ['. Nó cho thấy Tiện ích kiểm tra đánh giá biểu thức và nếu nó đánh giá là true, trả về trạng thái thoát 0 (true); nếu không, nó trả về 1 (false). Nếu không có biểu thức nào, test cũng trả về 1 (false).
cavalcade

1

Có thể một cách tốt để ghi nhớ nó là:

  • Trả về mã trả lời "câu trả lời là gì?" true (hoặc kết quả khác) hoặc false
  • Mã thoát trả lời "vấn đề là gì?" thoát mã hoặc không có vấn đề (0)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.