การตั้งค่า pipefail สำหรับคำสั่ง piped เดียว


19

ฉันต้องการรันคำสั่งเชลล์ piped จำนวนหนึ่งจากสคริปต์ที่ไม่ใช่ BASH (เช่นสคริปต์ PHP) ดังนี้:

command1 | command2 | command3

ดังนั้นหากcommand1ล้มเหลวด้วยรหัสออกที่ไม่ใช่ศูนย์คำสั่งอื่น ๆ ก็จะล้มเหลวเช่นกัน จนถึงตอนนี้สิ่งที่ฉันเกิดขึ้นคือ:

set -o pipefail && command1 | command2 | command3

แม้ว่ามันจะทำงานได้ดีจากเทอร์มินัล แต่จะสร้างสิ่งนี้หากเรียกใช้งานจากสคริปต์:

sh: 1: set: ตัวเลือกที่ผิดกฎหมาย -o pipefail


ปรากฏว่าไม่ชอบ/bin/sh set -o pipefailจริง ๆ แล้วมันbashปลอมตัวหรือเป็นเปลือกที่แตกต่างกัน? เมื่อbashถูกเรียกใช้/bin/shจะยอมรับset -o pipefailหรือไม่
Jonathan Leffler

@JanathanLeffler เมื่อฉันเรียกใช้/bin/sh set -o pipefailมันจะบอกว่า/bin/sh: 0: Can't open set(สิ่งเดียวกันกับsudo) หวังว่าฉันจะทดสอบมันถูกต้อง ระบบคือ Ubuntu 12
Desmond Hume

คุณจะต้องลอง/bin/sh -c "set -o pipefail"; อย่างที่มันเป็นเชลล์พยายามรันสคริปต์ในไดเรกทอรีปัจจุบันที่เรียกว่าsetและไม่พบมัน
Jonathan Leffler

@JonathanLeffler /bin/sh -c "set -o pipefail"ไม่ทำงาน แต่bash -c "set -o pipefail"ทำไม่ได้
Desmond Hume

ดังนั้นปัญหาของคุณคือว่าสคริปต์จะดำเนินการโดยที่ไม่รู้จัก/bin/sh set -o pipefailดังนั้นคุณจะต้องให้แน่ใจว่าสคริปต์ที่จะดำเนินการโดยแทน/bin/bash /bin/shหรือหากคุณมั่นใจกล้าหาญและอาจบ้าบิ่น - เปลี่ยน/bin/shเป็นลิงก์หรือคัดลอก/bin/bashแทนที่จะเป็นเชลล์ใดก็ตามที่มันเชื่อมโยงอยู่หรือคัดลอก หากคุณแน่ใจว่า/bin/shเป็นbashเช่นนั้นคุณกำลังใช้พฤติกรรมที่bashไม่เปิดเผยเมื่อเรียกใช้เป็น/bin/sh; ใช้เป็นbash bash
Jonathan Leffler

คำตอบ:


18

จากบรรทัดคำสั่ง Bash คุณจะต้องเรียกใช้ subshell เพื่อหลีกเลี่ยงpipefailการตั้งค่าในภายหลัง:

$ (set -o pipefail && command1 | command2 | command3)

ซึ่งจะเป็นการ จำกัด ผลกระทบของpipefailตัวเลือกในการ subshell (...)สร้างโดยวงเล็บ

ตัวอย่างจริง:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

ถ้าคุณใช้โทรเปลือกอย่างชัดเจนกับ-cตัวเลือกที่คุณไม่จำเป็นต้อง subshell ทั้งที่มีbashหรือที่มีshนามแฝงเพื่อbash:

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

เนื่องจากคุณshไม่ยอมรับpipefailตัวเลือกฉันจะต้องสมมติว่ามันเป็นรุ่นที่เก่ากว่าหรือแก้ไขแล้วbash- หรือว่าเป็นเชลล์อื่นที่สมบูรณ์


1
อยู่$ในส่วนแรกส่วนหนึ่งของคำสั่งมันเป็นเพียงเปลือกให้ พยายามทั้งสองวิธีไม่ได้ผลจริงๆ bash -cวิธีการทำงานมักจะไม่สง่างามสรรพสินค้าใหญ่มากเกินไป ..
เดสมอนด์ฮูม

5

ไม่แน่ใจว่าทำไมมันก็ไม่ได้กล่าวถึงข้างต้น แต่มันก็เป็นไปได้ที่จะไม่มีการตั้งค่าอย่างชัดเจนโดยใช้pipefailset +o pipefail

set -o pipefail
command1 | command2 | command3
set +o pipefail

หากคุณกำลังดำเนินการชิ้นส่วนและไม่แน่ใจเกี่ยวกับการpipefailตั้งค่าแล้วคุณสามารถใช้สิ่งนี้กับ subshell ตามที่แนะนำก่อนหน้านี้:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )

0

คุณสามารถใช้ $ (command1) รวมกับ $ ได้หรือไม่ :

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

พิมพ์ "abc"

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

พิมพ์ ""

"[$? -eq 0] &&" หมายความว่าคำสั่งต่อไปนี้จะถูกดำเนินการเฉพาะเมื่อคำสั่งก่อนหน้าสำเร็จ

นั่นไม่ได้ตอบคำถามของคุณ แต่เป็นวิธีแก้ปัญหาของคุณ


มันหมายความว่าฉันจะต้องเก็บเอาท์พุทของแต่ละคำสั่งไว้ในตัวแปรเสมอหรือไม่?
Desmond Hume

ฉันไม่รู้เสมอ แต่มีวิธีแก้ปัญหาของฉันใช่คุณจะ (ถ้าคุณต้องการสำหรับคำสั่งถัดไป) ถ้าคุณสามารถเขียนcommand1 && command2 && command3
cglacet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.