ความหมายของ $? (เครื่องหมายคำถามดอลลาร์) ในเชลล์สคริปต์


คำตอบ:


212

นี่คือสถานะการออกของคำสั่งที่ดำเนินการครั้งสุดท้าย

ตัวอย่างเช่นคำสั่งtrueส่งคืนสถานะเสมอ0และfalseส่งคืนสถานะเสมอ1:

true
echo $? # echoes 0
false
echo $? # echoes 1

จากคู่มือ: (acessible โดยการโทรman bashในเชลล์ของคุณ)

$?       ขยายเป็นสถานะออกของไปป์ไลน์เบื้องหน้าที่ดำเนินการล่าสุด

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

มีตัวแปรพิเศษอื่น ๆ เช่นนี้อย่างที่คุณเห็นในคู่มือออนไลน์นี้: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


หมายเหตุ$และ?เป็นสองพารามิเตอร์ที่แตกต่างและ$?ไม่ปรากฏใน bash (1) manpage
Josh Habdas

19

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

ดังนั้นเมื่อสคริปต์ ฉันมักจะใช้ไวยากรณ์ต่อไปนี้

if [ $? -eq 0 ]; then
 # do something
else
 # do something else
fi

การเปรียบเทียบจะต้องทำในเท่ากับ0หรือไม่เท่ากับ0หรือไม่เท่ากับ

** อัปเดตตามความคิดเห็น: โดยหลักการแล้วคุณไม่ควรใช้โค้ดบล็อกด้านบนเพื่อทำการเปรียบเทียบดูที่ความคิดเห็นและคำอธิบาย @tripleee


15
ไม่นี่คือปฏิภาณ สิ่งใดซึ่งมีลักษณะเหมือนที่ควรจะไปcmd; if [ $? -eq 0 ]; then refactored วัตถุประสงค์if cmd; thenอย่างมากของ(และคำสั่งควบคุมการไหลอื่น ๆ ในเปลือก) คือการเรียกใช้คำสั่งและตรวจสอบสถานะการออก if
tripleee

if cmd;อาจไม่สามารถอ่านได้มากบางเงื่อนไขโดยเฉพาะอย่างยิ่งเมื่อ cmd อ้างถึงสคริปต์อื่น
Saurabh Ariyan

1
นี่มันผิดมากขึ้นแล้ว [ 1 ]และ[ 0 ]เป็นทั้งจริง [ไม่มีตัวดำเนินการตรวจสอบว่าอาร์กิวเมนต์เป็นสตริงที่ไม่ว่างเปล่า
tripleee

2
vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;ฉันจะทำอย่างไร หากฉันต้องใส่มันในบรรทัดเดียวif [ ... ]มันจะอ่านไม่ได้ชะมัด ฉันวางแผนที่จะเก็บเอาท์พุทของบรรทัดนั้นไปยังตัวแปรดังนั้นฉันจึงสามารถพูดได้ในif [ $drupal_installed -eq 0 ]ภายหลัง
สาม

1
@thirdender วิธีแก้ปัญหาที่เหมาะสมคือการห่อหุ้มการทดสอบที่ซับซ้อนในฟังก์ชั่นเชลล์
tripleee

12

echo $ - ให้ออกจากสถานะของคำสั่งดำเนินการมากที่สุดเมื่อเร็ว ๆ นี้ สถานะ EXIT นี้อาจเป็นตัวเลขที่มีค่าเป็นศูนย์ซึ่งหมายความว่าสำเร็จและค่าที่ไม่ใช่ศูนย์ใด ๆ ที่แสดงถึงความล้มเหลว

? - นี่คือหนึ่งพารามิเตอร์ / ตัวแปรพิเศษในทุบตี

$?- มันให้ค่าที่เก็บไว้ในตัวแปร "?"

พารามิเตอร์พิเศษที่คล้ายกันบางรายการใน BASH คือ 1,2, *, # (ปกติจะเห็นในคำสั่ง echo เป็น $ 1, $ 2, $ *, $ #, ฯลฯ )



5

ตัวอย่างสถานะการออก POSIX C ขั้นต่ำสุด

เพื่อให้เข้าใจ$?ก่อนอื่นคุณต้องเข้าใจแนวคิดของกระบวนการสถานะทางออกซึ่งถูกกำหนดโดย POSIX ใน Linux:

  • เมื่อกระบวนการเรียกการเรียกของexitระบบเคอร์เนลจะเก็บค่าที่ส่งผ่านไปยังการเรียกของระบบ (an int) แม้หลังจากกระบวนการจะตาย

    เรียกระบบทางออกถูกเรียกโดยexit()ฟังก์ชั่นมาตรฐาน ANSI C, และทางอ้อมเมื่อคุณทำจากreturnmain

  • กระบวนการที่เรียกว่ากระบวนการลูกออก (Bash) มักจะมีfork+ execสามารถดึงสถานะออกของเด็กด้วยการwaitเรียกระบบ

พิจารณารหัส Bash:

$ false
$ echo $?
1

C "เทียบเท่า" คือ:

false.c

#include <stdlib.h> /* exit */

int main(void) {
    exit(1);
}

bash.c

#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */

int main(void) {
    if (fork() == 0) {
        /* Call false. */
        execl("./false", "./false", (char *)NULL);
    }
    int status;
    /* Wait for a child to finish. */
    wait(&status);
    /* Status encodes multiple fields,
     * we need WEXITSTATUS to get the exit status:
     * http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
     **/
    printf("$? = %d\n", WEXITSTATUS(status));
}

รวบรวมและเรียกใช้:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash

เอาท์พุท:

$? = 1

ใน Bash เมื่อคุณกด Enter ส้อม + exec + รอจะเกิดขึ้นเหมือนด้านบนและทุบตีจากนั้นกำหนด$?เป็นสถานะทางออกของกระบวนการแยก

หมายเหตุ: สำหรับคำสั่งในตัวเช่นechoกระบวนการไม่จำเป็นต้องวางไข่และ Bash เพียงตั้งค่า$?เป็น 0 เพื่อจำลองกระบวนการภายนอก

มาตรฐานและเอกสาร

POSIX 7 2.5.2 "พารามิเตอร์พิเศษ" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? ขยายเป็นสถานะออกทศนิยมของไปป์ไลน์ล่าสุด (ดู Pipelines)

man bash "พารามิเตอร์พิเศษ":

เชลล์ปฏิบัติต่อพารามิเตอร์หลายอย่างเป็นพิเศษ พารามิเตอร์เหล่านี้สามารถอ้างอิงได้เท่านั้น ไม่อนุญาตให้มอบหมายงานให้ [ ... ]

? ขยายเป็นสถานะออกของไปป์ไลน์เบื้องหน้าที่ดำเนินการล่าสุด

ANSI C และ POSIX แนะนำให้ทำดังนี้:

  • 0 หมายความว่าโปรแกรมประสบความสำเร็จ

  • ค่าอื่น ๆ : โปรแกรมล้มเหลวอย่างใด

    ค่าที่แน่นอนสามารถระบุประเภทของความล้มเหลว

    ANSI C ไม่ได้กำหนดความหมายของ vaues ใด ๆ และ POSIX ระบุค่าที่มากกว่า 125: ความหมายของ "POSIX" คืออะไร?

Bash ใช้สถานะทางออกสำหรับ if

ใน Bash เรามักจะใช้สถานะออก$?โดยปริยายเพื่อควบคุมifคำสั่งใน:

if true; then
  :
fi

โดยที่trueโปรแกรมนั้นจะคืนค่า 0

ข้างต้นเทียบเท่ากับ:

true
result=$?
if [ $result = 0 ]; then
  :
fi

และใน:

if [ 1 = 1 ]; then
  :
fi

[เป็นเพียงโปรแกรมที่มีชื่อแปลก ๆ (และ Bash ในตัวที่ทำงานเหมือนมัน) และ1 = 1 ]การขัดแย้งดูได้ที่: ความแตกต่างระหว่างวงเล็บเหลี่ยมเดี่ยวและคู่ใน Bash



3

ดูคู่มือทุบตีภายใต้3.4.2 พารามิเตอร์พิเศษ :

? - ขยายเป็นสถานะออกของไปป์ไลน์เบื้องหน้าที่ดำเนินการล่าสุด

มันหายากนิดหน่อยเพราะไม่มีอยู่ในรายการ$?(ชื่อตัวแปรคือ "just" ?) ดูสถานะทางออกหัวข้อแน่นอน ;-)

การเข้ารหัสที่มีความสุข


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