ฉันจะรับเอาท์พุทและมูลค่าการออกของ subshell เมื่อใช้“ bash -e” ได้อย่างไร?


70

พิจารณารหัสต่อไปนี้

outer-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("

inner-scope.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }

ฉันพยายามouter-scope.shออกเมื่อสายinner()ล้มเหลว ตั้งแต่$()เรียกใช้ sub-shell สิ่งนี้จะไม่เกิดขึ้น

ฉันจะได้รับผลลัพธ์ของฟังก์ชันในขณะที่รักษาความจริงที่ว่าฟังก์ชันอาจออกด้วยรหัสออกที่ไม่ใช่ศูนย์

คำตอบ:


102

$()รักษาสถานะการออก คุณเพียงแค่ต้องใช้มันในแถลงการณ์ที่ไม่มีสถานะเป็นของตนเองเช่นการมอบหมาย

เอาท์พุท = $ (ภายใน)

หลังจากนี้$?จะมีสถานะออกจากinnerและคุณสามารถใช้การตรวจสอบได้ทุกประเภท:

output=$(inner) || exit $?
echo $output

หรือ:

if ! output=$(inner); then
    exit $?
fi
echo $output

หรือ:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(หมายเหตุ: เปล่าexitโดยไม่มีข้อโต้แย้งเทียบเท่ากับexit $?- นั่นคือมันออกจากสถานะทางออกของคำสั่งสุดท้ายฉันใช้รูปแบบที่สองเท่านั้นเพื่อความชัดเจน)


นอกจากนี้สำหรับบันทึก: sourceไม่เกี่ยวข้องอย่างสมบูรณ์ในกรณีนี้ คุณสามารถกำหนดinner()ในouter-scope.shไฟล์ด้วยผลลัพธ์เดียวกัน


ทำไมถึงเป็นเช่นนั้นแม้ว่า $ มีสถานะออกจาก $ () ที่สคริปต์ไม่ออกโดยอัตโนมัติ (กำหนดว่า -e ถูกตั้งค่า)? แก้ไข: ไม่เป็นไรฉันคิดว่าคุณได้ตอบคำถามของฉันขอบคุณ!
jabalsad

ฉันไม่แน่ใจ. (ฉันไม่ได้ทำการทดสอบใด ๆ ข้างต้น) แต่มีข้อ จำกัด บางประการเกี่ยวกับ -e ทั้งหมดอธิบายไว้ใน manpage ของ bash; นอกจากนี้หากคุณถามถึงecho $()อาจเป็นเพราะรหัสทางออกของ subshells จะถูกละเว้นเมื่อบรรทัด - echoคำสั่ง - มีรหัสออก (ปกติ 0) ของตัวเอง
grawity

1
อืมเมื่อฉันพิมพ์ฉันได้รับif ! $(exit 1) ; then echo $?; fi 0ไม่แน่ใจว่าifเป็นวิธีที่จะไปหากคุณต้องการรักษามูลค่าการออกจากนั้น
Ron Burk

@grawity - ใช้งานได้กับ Dash หรือไม่ ฉันพยายามที่จะหลีกเลี่ยงพฤติกรรม Autoconf ที่น่ารำคาญ (กล่าวคือ Autoconf รายงานความสำเร็จเมื่อ Sun หรือ IBM คอมไพเลอร์พิมพ์ตัวเลือกผิดกฎหมายไปยังเทอร์มินัล)
jww

3
if ! output=$(inner); then exit $?; fiจะออกด้วยรหัสส่งคืนเป็น 0 เพราะ$?จะให้รหัสส่งคืน!แทนรหัสส่งคืนinnerเป็น คุณสามารถได้รับพฤติกรรมที่ต้องการด้วยif output=$(inner); then : ; else exit $?; fiแต่นั่นชัดเจนมากขึ้น
SJL

26

ดูBashFAQ / 002 :

หากคุณต้องการทั้ง (เอาท์พุทและสถานะทางออก):

output=$(command)
status=$? 

กรณีพิเศษ

ทราบเกี่ยวกับกรณีที่ยุ่งยากกับตัวแปรท้องถิ่นฟังก์ชั่นเปรียบเทียบรหัสต่อไปนี้:

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

เราจะได้รับ:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1

ทำไม?

เมื่อการส่งออกของ subshell ที่จะใช้ในการเริ่มต้นlocalตัวแปรออกจากสถานะที่ไม่มีของ subshell แต่ของคำสั่งซึ่งเป็นส่วนใหญ่มีแนวโน้มที่จะเป็นlocal 0

ดูเพิ่มเติมที่https://stackoverflow.com/a/4421282/537554


3
แม้ว่าสิ่งนี้จะไม่ตอบคำถามจริงๆ แต่สิ่งนี้มีประโยชน์สำหรับฉันในวันนี้ดังนั้น +1
fourpastmidnight

1
สถานะการออกสำหรับคำสั่ง bash จะเป็นของคำสั่งสุดท้ายที่ถูกเรียกใช้งานเสมอ เมื่อเราใช้เวลามากในภาษาที่พิมพ์อย่างรุนแรงมันง่ายที่จะลืมว่า "local" ไม่ใช่ตัวระบุประเภท แต่เป็นเพียงคำสั่งอื่น ขอบคุณสำหรับการทำซ้ำจุดนี้ที่นี่ช่วยฉันวันนี้
markeissler

2
ว้าวฉันเจอปัญหาตรงนี้แล้วตอนนี้และเธอก็เคลียร์มันขึ้นมา ขอบคุณ!
krb686

4
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("

โดยการเพิ่มechosubshell ไม่โดดเดี่ยว (ไม่ได้ตรวจสอบแยกต่างหาก) และไม่ยกเลิก การมอบหมายหลีกเลี่ยงปัญหานี้

คุณยังสามารถทำสิ่งนี้และเปลี่ยนเส้นทางผลลัพธ์ไปยังไฟล์เพื่อดำเนินการในภายหลัง

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile

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