บันทึกรหัสทางออกของคำสั่งคล้ายกับคำสั่งเวลา


10

การใช้

time sleep 1

อัตราผลตอบแทน:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

มีคำสั่งที่ฉันสามารถใช้พิมพ์รหัสทางออกsleepหรือคำสั่งใดที่ฉันต้องการเรียกใช้หรือไม่

บางสิ่งที่ชอบ:

$ log-exit-code sleep 1

อาจเพียงพอหรือไม่

sleep 1 && echo "$?"

3
Nope sleep 1 && echo $?จะพิมพ์รหัสของเซลล์ sleeper เมื่อมันเป็นศูนย์ ...
Satō Katsura

โอ้คุณพูดถูกแล้ว
Alexander Mills

1
sleep 1 && echo "$?" || echo "$?"
jesse_b

2
ฉันได้เพิ่มคำตอบเนื่องจากคนอื่น ๆ ทิ้งค่าส่งคืนดั้งเดิมซึ่ง imo ไม่ดี (หลังจากคำตอบเหล่านั้น $? เสมอ 0 เพราะสิ่งสุดท้ายที่พวกเขาทำคือ disply ซึ่งใช้งานได้เสมอพวกเขาลืมเก็บไว้ ค่าส่งคืนต้นฉบับและส่งคืนหลังจากแสดงดังนั้นพวกเขาเพียงแสดงให้ผู้สังเกตการณ์ แต่ 'ซ่อน' เป็นส่วนที่เหลือของสคริปต์ซึ่งไม่สามารถใช้ตอนนี้ "$?" เพื่อดูว่าคำสั่งที่สำคัญกลับมา 0 หรืออย่างอื่น ... )
Olivier Dulac

1
โซลูชันทั้งหมดด้านล่างนี้สามารถใช้ฟังก์ชันนี้ได้อย่างง่ายดาย ตัวอย่างของ Kusalananda จะเป็นเพียง:EXIT_CODE=$(tellexit command)
jesse_b

คำตอบ:


4

ในการทดสอบของฉันจนถึงตอนนี้สิ่งนี้ได้ผล:

command && echo "$?" || echo "$?"

เพียงบอกให้ echo รหัสทางออกถ้าสำเร็จหรือล้มเหลว

ดังที่ Sato ชี้ให้เห็นด้านล่างนี้เป็นหลักเหมือนกับ:

command; echo "$?"

สิ่งหนึ่งที่สามารถสร้างและ / หรือคำสั่งที่คุ้มค่าคือ:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

หากคุณต้องการสคริปต์ของคุณเพื่อดำเนินการกับรหัสออกเช่นเดียวกับความกังวลของ Olivier มันไม่ใช่ปัญหา สคริปต์ของคุณอาจมีลักษณะดังนี้:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac

13
เอ่อนั่นเป็นวิธีที่ดีกว่าเพียงแค่command; echo $??
Satō Katsura

4
ฉันเดาว่ามันไม่ใช่ command; echo $?เป็นคำตอบที่ดีกว่ามาก
jesse_b

uhhh นี้ดูเหมือนว่าเป็นตัวเลือกที่ดีที่สุดดังนั้นฉันเลือกมันเป็นคำตอบถ้าใครไม่เห็นด้วย lmk
อเล็กซานเดอร์มิลส์

@AlexanderMills มีเพียงคุณเท่านั้นที่รู้ว่าคำตอบที่เหมาะสมที่สุดสำหรับคำถามของคุณคือ :-)
Kusalananda

1
ฉันไม่ชอบสิ่งนี้: นี่เป็นการเปลี่ยนแปลงรหัสออกหลังจากcommand ; echo $?เพราะecho $?สำเร็จเสมอและหลังจากนั้นค่าใหม่ของ $ คืออะไร? ตอนนี้คือ 0 มีวิธีเก็บรหัสส่งคืนดั้งเดิมไว้แม้ว่าจะแสดงอยู่ในกรณีที่รหัสส่งคืนดั้งเดิมนี้มีประโยชน์ในภายหลัง (ตัวอย่างเช่น: ในสคริปต์การตรวจสอบรหัสส่งคืนนั้นสำคัญก่อนที่จะตัดสินใจดำเนินการต่อ) ดูคำตอบของฉันสำหรับหนึ่งในหลาย ๆ วิธีแก้ไข (คำตอบส่วนใหญ่ที่นี่สามารถเปลี่ยนแปลงได้ในลักษณะเดียวกัน)
Olivier Dulac

11

cmd && echo "$?"จะไม่ทำงานเนื่องจากจำเป็นต้องพิมพ์เลขศูนย์เท่านั้น ( echoจะดำเนินการเฉพาะเมื่อสำเร็จคำสั่งก่อนหน้านี้เท่านั้น)

นี่คือฟังก์ชั่นเปลือกสั้นสำหรับคุณ:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

สิ่งนี้จะพิมพ์รหัสออกของคำสั่งที่กำหนดในลักษณะที่คล้ายกับtimeคำสั่ง

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

โดยการเปลี่ยนเส้นทางprintfไปยัง/dev/ttyในฟังก์ชั่นเรายังคงสามารถใช้tellexitกับการเปลี่ยนเส้นทางโดยไม่ได้รับขยะในเอาต์พุตมาตรฐานหรือสตรีมข้อผิดพลาด:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

ด้วยการบันทึกรหัสการออกในตัวแปรเราสามารถคืนค่ามันไปยังผู้เรียก:

$ tellexit false || echo 'failed'
exit code       1
failed

ฟังก์ชั่นรุ่นเดียวกันที่ดีกว่านี้จะพิมพ์สัญญาณที่ฆ่าคำสั่งหากรหัสออกมีค่ามากกว่า 128 (ซึ่งหมายความว่าจะสิ้นสุดลงเนื่องจากสัญญาณ):

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

การทดสอบ:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

( localสิ่งนี้ต้องการash/ pdksh/ bash/ zshหรือคุณสามารถเปลี่ยนให้typesetเชลล์อื่นเข้าใจได้สองสาม)


เจ๋ง, คุณจะใช้ tellexit อย่างไร, แสดงให้เห็นว่ามันใช้งานได้จริง
Alexander Mills

1
@AlexanderMills ดูการอัปเดต
Kusalananda

2
ควรเก็บค่าส่งคืนดั้งเดิมและส่งคืนในที่สุด
Olivier Dulac

@OlivierDulac สังเกตได้ดีจริงๆ! ฉันจะแก้ไข
Kusalananda

7

ใช้ฟังก์ชัน wrapper ของเชลล์ อาจมีชื่อแตกต่างกัน

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(หลักสูตรนี้จะทำให้เอาต์พุตมาตรฐานเสียหายดังนั้นควรใช้ttyตามที่แสดงโดย @Kusalananda หรือไม่ใช้นอกบริบทแบบโต้ตอบ)

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

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR มิฉะนั้นจะไม่เริ่มทำงานเมื่อไม่มีข้อผิดพลาด (ในส่วนของ "ไม่มีข่าวที่เป็นข่าวที่ดี")


1
ควรเก็บค่าส่งคืนดั้งเดิมและส่งคืนในที่สุด การแสดง (printf, echo) เป็นสิ่งที่ง่าย (ไม่ได้ขึ้นต้นด้วย '-' ฯลฯ ) ใช้งานได้ทุกครั้งและหลังจากที่แสดง "$" ตอนนี้มีค่าเป็น "0" ขณะที่จอแสดงผลทำงาน
Olivier Dulac

7

GNU timeมีตัวเลือกสำหรับสิ่งนี้:

time -f %x sleep 1
0

ผ่านรหัสทางออก1ยกเว้นว่าสัญญาณถูกฆ่า2 :

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

หากคุณต้องการที่จะรู้ / การจัดการสัญญาณฆ่าสถานการณ์ผ่าน-vและ grep stderr Command terminated by signalสำหรับสตริง


1 ต้องขอขอบคุณ Olivier Dulac สำหรับการสังเกตรหัสการส่งผ่าน
2นอกจากนี้ขอขอบคุณStéphane Chazelas ที่ชี้ให้เห็นว่ารหัสทางออกของสัญญาณฆ่าไม่ผ่าน


1
interresting GNU เท่านั้น แต่เป็นการหยุดยั้งเนื่องจากจะมีการส่งคืนรหัสต้นฉบับสำหรับรหัสที่เหลือเพื่อติดตาม (เช่นจะไม่แทนที่ด้วย "0" ตามคำตอบอื่น ๆ )
Olivier Dulac

2
มันจะส่งกลับ0แม้ว่าคำสั่งถูกฆ่า
Stéphane Chazelas

2
@ บิชอปฉันเห็นด้วย 0 ในกรณีเหล่านั้นน้อยกว่าอุดมคติ แต่โปรดทราบว่าไม่มีข้อตกลงทั่วไปเกี่ยวกับสิ่งที่ต้องรายงานเมื่อคำสั่งถูกฆ่าโดยสัญญาณ กระสุนที่ส่งกลับหมายเลข 128+ นั้นถูกใช้โดยเปลือกบางส่วน คุณจะเห็นสิ่งต่าง ๆ เช่น256+signumหรือ384+signumแม้กระทั่งsignum/256หรือsignum/256+0.5ถ้าแกนถูกสร้างขึ้น (จริง ๆ แล้วสถานะที่ส่งคืนโดยwaitpid()หารด้วย 256) บางคนจะให้การแสดงข้อความ (เช่นsigintหรือsigquit+core) ยังคงมีมูลค่าการพูดคุยเกี่ยวกับกลุ่มข่าว gnu.coreutils.bugs ฉันเห็นด้วย
Stéphane Chazelas

3
(อันที่จริงtimeไม่ได้เป็นส่วนหนึ่งของcoreutilsมันเป็นแพคเกจของตัวเองที่มีรายชื่อผู้รับจดหมายของตัวเอง (bug-time@gnu.org))
Stéphane Chazelas

1
@ บิชอป, awks system()แบบดั้งเดิมเหมือนที่ยังคงได้รับการดูแลโดย Brian Kernighan ( kในawk) หรือnawkของ Solaris, awkของ FreeBSD ที่ได้มาจากสิ่งนั้น awk 'BEGIN{print system("kill -s ABRT $$")}'ส่งออก0.523438เมื่อขนาดการถ่ายโอนข้อมูลหลักไม่ จำกัด พฤติกรรมของgawkมีการเปลี่ยนแปลงเมื่อเร็ว ๆ นี้กับพฤติกรรมที่แตกต่างกันด้วย--traditionalหรือ--posix(ดูค่าตอบแทนclose(cmd)สำหรับการเปลี่ยนแปลงมากขึ้น)
Stéphane Chazelas

2

ฉันไม่ชอบคำตอบอื่น ๆ ทั้งหมด (แต่ฉันชอบความฉลาดของพวกเขามาก): พวกเขาแสดงรหัสออก แต่พวกเขาก็เปลี่ยนมันด้วย (ส่วนที่แสดงเป็นจริงดังนั้นหลังจากส่งคืนรหัสคือ 0)

นี่คือรุ่นที่แก้ไข:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1

จริงๆแล้ว @bishop ตอบเป็นอีกข้อเดียวที่รักษาคุณค่านี้ไว้ แต่ต้องอาศัยเวอร์ชันของ GNU timeซึ่งไม่สามารถใช้ได้ตลอดเวลา
Olivier Dulac

2

เพื่อให้มีความทนทานมากกว่านี้เล็กน้อยให้ส่งสถานะการออกไปยัง FD ที่แยกต่างหากเพื่อให้สามารถจัดการได้อย่างเป็นอิสระจาก stdout / stderr:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

หมายเหตุคุณจะต้องทำอะไรกับ fd3 Bad file descriptorหรือมันจะบอกว่า

สิ่งนี้สามารถกำหนดค่าเพิ่มเติมเพื่อให้ผลลัพธ์เหล่านี้ไปยังอินพุตของโปรแกรมอื่นโดยให้โปรแกรมอื่นฟังมากกว่าหนึ่ง FD; คุณสามารถใช้สิ่งนี้เป็น Kibana ช่วงต้นยุค 90 ได้ :)


ควรเก็บค่าส่งคืนดั้งเดิมและส่งคืนในที่สุด การแสดง (printf, echo) เป็นสิ่งที่ง่าย (ไม่ได้ขึ้นต้นด้วย '-' ฯลฯ ) ใช้งานได้ทุกครั้งและหลังจากที่แสดง "$" ตอนนี้มีค่าเป็น "0" ขณะที่จอแสดงผลทำงาน
Olivier Dulac

1

ในขณะที่คนอื่น ๆ (น่าสนใจมาก) ตอบคำถามตรงตามที่ OP ถาม แต่ในทางปฏิบัติสิ่งต่าง ๆ มักจะง่ายกว่า คุณเพียงแค่บันทึกสถานะการออกไปยังตัวแปร (ทำตามคำสั่งทันที) และรายงานหรือบันทึกในแบบที่คุณต้องการ เช่นพิมพ์ไปที่ / dev / stderr หรือเขียน / ผนวกเป็นข้อความตัวอักษรลงในไฟล์บันทึก และมันยังถูกเก็บรักษาไว้เพื่อใช้ในการตัดสินใจ / ควบคุมการไหลในรหัสที่ตามมา

หากอยู่ในฟังก์ชั่น:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

หากอยู่ในสคริปต์โดยตรง:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(การปิดและปิดบังเนื่องจากสิ่งนี้ไม่ตอบคำถามที่แน่นอน

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