วิธีเก็บข้อผิดพลาดมาตรฐานในตัวแปร


182

สมมติว่าฉันมีสคริปต์ดังต่อไปนี้:

useless.sh

echo "This Is Error" 1>&2
echo "This Is Output" 

และฉันมีเชลล์สคริปต์อีกอันหนึ่ง:

alsoUseless.sh

./useless.sh | sed 's/Output/Useless/'

ฉันต้องการจับภาพ "นี่คือข้อผิดพลาด" หรือ stderr อื่น ๆ จาก useless.sh ไปเป็นตัวแปร ลองเรียกมันว่า ERROR

สังเกตว่าฉันกำลังใช้ stdout สำหรับบางสิ่ง ฉันต้องการใช้ stdout ต่อไปดังนั้นการเปลี่ยนเส้นทาง stderr ไปยัง stdout จะไม่เป็นประโยชน์ในกรณีนี้

โดยพื้นฐานแล้วฉันต้องการจะทำ

./useless.sh 2> $ERROR | ...

แต่เห็นได้ชัดว่าใช้งานไม่ได้

ฉันก็รู้ว่าฉันสามารถทำได้

./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`

แต่มันก็น่าเกลียดและไม่จำเป็น

น่าเสียดายถ้าไม่มีคำตอบปรากฏขึ้นนี่คือสิ่งที่ฉันจะต้องทำ

ฉันหวังว่าจะมีวิธีอื่น

ใครมีแนวคิดที่ดีกว่านี้บ้าง?


4
ต้องการใช้ stdout เพื่ออะไร คุณเพียงแค่ต้องการดูมันบนคอนโซล? หรือคุณจับภาพ / เปลี่ยนเส้นทางมันเอาท์พุท? หากเป็นเพียงคอนโซลคุณเปลี่ยนเส้นทาง stdout เพื่อ console และ stderr เพื่อ stdout เพื่อจับมัน:ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Tim Kersten

คำตอบ:


91

มันจะเป็น neater เพื่อจับไฟล์ข้อผิดพลาดจึง:

ERROR=$(</tmp/Error)

เชลล์รับรู้สิ่งนี้และไม่จำเป็นต้องเรียกใช้ ' cat' เพื่อรับข้อมูล

คำถามที่ใหญ่กว่านั้นยาก ฉันไม่คิดว่าจะมีวิธีง่ายๆในการทำเช่นนั้น คุณต้องสร้างไปป์ไลน์ทั้งหมดลงในเชลล์ย่อยในที่สุดก็ส่งเอาต์พุตมาตรฐานสุดท้ายไปยังไฟล์เพื่อให้คุณสามารถเปลี่ยนเส้นทางข้อผิดพลาดไปยังเอาต์พุตมาตรฐานได้

ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )

โปรดทราบว่าจำเป็นต้องใช้เซมิโคลอน (ในกระสุนคลาสสิก - Bourne, Korn - แน่นอน; อาจจะอยู่ใน Bash ด้วย) การ{}เปลี่ยนทิศทาง I / O ผ่านคำสั่งที่ล้อมรอบ ตามที่เขียนไว้ก็จะจับข้อผิดพลาดจากsedด้วย

คำเตือน:รหัสที่ยังไม่ได้ทดสอบอย่างเป็นทางการ - ใช้โดยยอมรับความเสี่ยงเอง


1
ฉันหวังว่าจะมีเคล็ดลับบ้า ๆ บอ ๆ ที่ฉันไม่รู้ แต่ดูเหมือนว่านี่มัน ขอบคุณ
psycotica0

9
หากคุณไม่ต้องการเอาต์พุตมาตรฐานคุณสามารถเปลี่ยนเส้นทางไปที่/dev/nullแทนที่จะoutfile(ถ้าคุณชอบฉันคุณพบคำถามนี้ผ่าน Google และไม่มีข้อกำหนดเดียวกับ OP)
Mark Eirich

2
สำหรับคำตอบโดยไม่ต้องไฟล์ชั่วคราวให้ดูที่นี่
Tom Hale

1
นี่คือวิธีการทำโดยไม่เปลี่ยนเส้นทางไปยังไฟล์ มันเล่นกับการแลกเปลี่ยนstdoutและstderrออกมาและกลับมา แต่ระวังเป็นที่นี่เป็นที่กล่าวว่า: ในทุบตีมันจะดีกว่าที่จะไม่คิดว่าอธิบายไฟล์ 3 ไม่ได้ใช้" .
Golar Ramblar

69

alsoUseless.sh

นี้จะช่วยให้คุณสามารถท่อส่งออกของคุณuseless.shสคริปต์ผ่านคำสั่งเช่นsedและบันทึกในชื่อตัวแปรstderr errorผลลัพธ์ของไพพ์จะถูกส่งไปยังstdoutเพื่อแสดงผลหรือจะถูกไพพ์ไปยังคำสั่งอื่น

ตั้งค่าตัวอธิบายไฟล์เพิ่มเติมสองสามตัวเพื่อจัดการการเปลี่ยนเส้นทางที่จำเป็นเพื่อดำเนินการนี้

#!/bin/bash

exec 3>&1 4>&2 #set up extra file descriptors

error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )

echo "The message is \"${error}.\""

exec 3>&- 4>&- # release the extra file descriptors

4
เป็นเทคนิคที่ดีในการใช้ 'exec' เพื่อตั้งค่าและปิดไฟล์ descriptors การปิดไม่จำเป็นจริงๆถ้าสคริปต์ออกจากทันทีหลังจากนั้น
Jonathan Leffler

3
ฉันจะจับทั้งสองstderrและstdoutในตัวแปรได้อย่างไร
Gingi

ยอดเยี่ยม สิ่งนี้ช่วยให้ฉันสามารถใช้dry_runฟังก์ชั่นที่สามารถเลือกได้อย่างน่าเชื่อถือระหว่างการสะท้อนข้อโต้แย้งของมันและเรียกใช้พวกเขาโดยไม่คำนึงว่าคำสั่งที่รันแบบแห้งกำลังถูกไพพ์ไปยังไฟล์อื่น
หมดเวลา Danila

1
@ t00bs: readไม่ยอมรับอินพุตจากไปป์ คุณสามารถใช้เทคนิคอื่น ๆ เพื่อให้ได้สิ่งที่คุณต้องการสาธิต
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

2
อาจจะง่ายกว่าด้วย: error = $ (./useless.sh | sed 's / Output / Useless /' 2> & 1 1> & 3)
Jocelyn

64

เปลี่ยนเส้นทาง stderr ไปที่ stdout, stdout เป็น / dev / null จากนั้นใช้ backticks หรือ$()เพื่อจับ stderr ที่เปลี่ยนเส้นทาง:

ERROR=$(./useless.sh 2>&1 >/dev/null)

8
นี่คือเหตุผลที่ฉันรวมไพพ์ไว้ในตัวอย่าง ฉันยังต้องการเอาต์พุตมาตรฐานและฉันต้องการให้ทำอย่างอื่นไปที่อื่น
psycotica0

สำหรับคำสั่งที่ส่งเอาต์พุตไปยัง stderr เท่านั้นวิธีง่ายๆในการจับภาพคือPY_VERSION="$(python --version 2>&1)"
John Mark

9

มีคำถามซ้ำกันมากมายสำหรับคำถามนี้ซึ่งส่วนใหญ่มีสถานการณ์การใช้งานที่เรียบง่ายขึ้นเล็กน้อยซึ่งคุณไม่ต้องการจับ stderr และ stdout และรหัสทางออกทั้งหมดพร้อมกัน

if result=$(useless.sh 2>&1); then
    stdout=$result
else
    rc=$?
    stderr=$result
fi

ใช้งานได้กับสถานการณ์ทั่วไปที่คุณคาดหวังว่าผลลัพธ์ที่เหมาะสมในกรณีที่ประสบความสำเร็จหรือข้อความวินิจฉัยบน stderr ในกรณีที่เกิดความล้มเหลว

โปรดสังเกตว่าคำสั่งควบคุมของเชลล์ตรวจสอบ$?ภายใต้ประทุนแล้ว ดังนั้นสิ่งที่ดูเหมือน

cmd
if [ $? -eq 0 ], then ...

เป็นเพียงวิธีการพูดที่งุ่มง่ามและไร้เหตุผล

if cmd; then ...

สิ่งนี้ใช้ได้กับฉัน: my_service_status = $ (บริการ my_service สถานะ 2> & 1) ขอบคุณ !!
JRichardsz

6
# command receives its input from stdin.
# command sends its output to stdout.
exec 3>&1
stderr="$(command </dev/stdin 2>&1 1>&3)"
exitcode="${?}"
echo "STDERR: $stderr"
exit ${exitcode}

1
commandเป็นตัวเลือกที่ไม่ดีที่นี่เนื่องจากมีตัวบิวอินด้วยชื่อนั้น อาจทำให้เป็นyourCommandเช่นนั้นเพื่อให้ชัดเจนยิ่งขึ้น
ชาร์ลส์ดัฟฟี่

4

เพื่อประโยชน์ของผู้อ่านสูตรนี้ค่ะ

  • สามารถนำมาใช้ใหม่เป็น oneliner เพื่อจับ stderr เป็นตัวแปร
  • ยังให้สิทธิ์การเข้าถึงโค้ดส่งคืนของคำสั่ง
  • สังเวยตัวอธิบายไฟล์ชั่วคราว 3 (ซึ่งแน่นอนว่าคุณสามารถเปลี่ยนแปลงได้)
  • และไม่แสดงตัวอธิบายไฟล์ชั่วคราวนี้ไปยังคำสั่งด้านใน

หากคุณต้องการที่จะจับstderrของบางส่วนcommandเข้าไปในvarที่คุณสามารถทำได้

{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;

หลังจากนั้นคุณมีทุกอย่าง:

echo "command gives $? and stderr '$var'";

หากcommandเป็นเรื่องง่าย (ไม่เหมือนa | b) คุณสามารถออกจากภายใน{}:

{ var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;

ห่อลงในbashฟังก์ชั่นที่ใช้ซ้ำได้ง่าย(อาจต้องใช้เวอร์ชัน 3 ขึ้นไปlocal -n):

: catch-stderr var cmd [args..]
catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }

อธิบาย:

  • local -nนามแฝง "$ 1" (ซึ่งเป็นตัวแปรสำหรับcatch-stderr)
  • 3>&1 ใช้ file descriptor 3 เพื่อบันทึก stdout points
  • { command; } (หรือ "$ @") จากนั้นดำเนินการคำสั่งภายในการจับผลลัพธ์ $(..)
  • โปรดทราบว่าคำสั่งที่แน่นอนมีความสำคัญที่นี่ (ทำในทางที่ผิดจะทำให้ตัวอธิบายไฟล์ผิด):
    • 2>&1เปลี่ยนเส้นทางstderrไปที่การจับภาพเอาท์พุท$(..)
    • 1>&3เปลี่ยนเส้นทางstdoutออกไปจากการจับเอาท์พุท$(..)กลับไปที่ "outer" stdoutซึ่งถูกบันทึกไว้ใน file descriptor 3 โปรดทราบว่าstderrยังคงอ้างอิงถึงตำแหน่งที่ FD 1 ชี้ไปก่อนหน้า: การจับภาพเอาต์พุต$(..)
    • 3>&-จากนั้นปิดไฟล์ descriptor 3 เนื่องจากไม่จำเป็นอีกต่อไปเช่นที่commandไม่ได้มีตัวอธิบายไฟล์เปิดที่ไม่รู้จักปรากฏขึ้น โปรดทราบว่าเปลือกนอกยังคงเปิด FD 3 แต่commandจะไม่เห็น
    • สิ่งหลังมีความสำคัญเนื่องจากบางโปรแกรมเช่นlvmบ่นเกี่ยวกับตัวอธิบายไฟล์ที่ไม่คาดคิด และlvmบ่นกับstderr- สิ่งที่เรากำลังจะจับ!

คุณสามารถจับไฟล์ descriptor อื่น ๆ ด้วยสูตรนี้หากคุณปรับตาม ยกเว้น file descriptor 1 แน่นอน (นี่คือตรรกะการเปลี่ยนเส้นทางจะผิด แต่สำหรับ file descriptor 1 คุณสามารถใช้งานได้var=$(command)ตามปกติ)

โปรดทราบว่าไฟล์นี้เสียสละตัวอธิบายไฟล์ 3 หากคุณต้องการตัวอธิบายไฟล์นั้นคุณสามารถเปลี่ยนหมายเลขได้ แต่ระวังให้ดีว่าบางหอย (จากปี 1980) อาจเข้าใจ99>&1ว่าเป็นข้อโต้แย้ง9ตามมาด้วย9>&1(นี่ไม่ใช่ปัญหาสำหรับbash)

โปรดทราบว่ามันไม่ใช่เรื่องง่ายที่จะทำให้ FD 3 สามารถกำหนดค่าได้ผ่านตัวแปร สิ่งนี้ทำให้สิ่งต่าง ๆ อ่านไม่ได้มาก:

: catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
catch-var-from-fd-by-fd()
{
local -n v="$1";
local fd1="$2" fd2="$3";
shift 3 || return;

eval exec "$fd2>&1";
v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
eval exec "$fd2>&-";
}

หมายเหตุด้านความปลอดภัย:catch-var-from-fd-by-fdไม่ควรนำอาร์กิวเมนต์ 3 ข้อแรกไปจากบุคคลที่สาม ให้พวกเขาอย่างชัดเจนในแบบ "คงที่"

ดังนั้นไม่ไม่ไม่catch-var-from-fd-by-fd $var $fda $fdb $commandไม่เคยทำเช่นนี้!

หากคุณบังเอิญผ่านชื่อตัวแปรอย่างน้อยก็ทำอย่างนี้: local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command

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

หมายเหตุ:

  • catch-var-from-fd-by-fd var 2 3 cmd.. เป็นเช่นเดียวกับ catch-stderr var cmd..
  • shift || returnเป็นเพียงวิธีการป้องกันข้อผิดพลาดน่าเกลียดในกรณีที่คุณลืมที่จะให้จำนวนที่ถูกต้องของการขัดแย้ง บางทีการยกเลิกเชลล์อาจเป็นอีกวิธีหนึ่ง (แต่สิ่งนี้ทำให้ยากต่อการทดสอบจาก commandline)
  • กิจวัตรประจำวันถูกเขียนขึ้นเพื่อให้เข้าใจได้ง่ายขึ้น เราสามารถเขียนฟังก์ชั่นใหม่ได้โดยที่ไม่ต้องการexecแต่ก็น่าเกลียดจริงๆ
  • ประจำวันนี้สามารถเขียนใหม่สำหรับที่ไม่ได้เป็นอย่างดีดังกล่าวว่าไม่มีความจำเป็นในการbash local -nอย่างไรก็ตามคุณไม่สามารถใช้ตัวแปรท้องถิ่นและมันน่าเกลียดมาก!
  • โปรดทราบว่าevalมีการใช้งานอย่างปลอดภัย มักevalถือว่าเป็นอันตราย อย่างไรก็ตามในกรณีนี้มันไม่ได้เลวร้ายยิ่งไปกว่าการใช้"$@"(เพื่อรันคำสั่งตามอำเภอใจ) อย่างไรก็ตามโปรดใช้ข้อความที่ถูกต้องและถูกต้องดังที่แสดงไว้ที่นี่ (ไม่เช่นนั้นจะกลายเป็นอันตรายมาก )

3

นี่คือวิธีที่ฉันทำ:

#
# $1 - name of the (global) variable where the contents of stderr will be stored
# $2 - command to be executed
#
captureStderr()
{
    local tmpFile=$(mktemp)

    $2 2> $tmpFile

    eval "$1=$(< $tmpFile)"

    rm $tmpFile
}

ตัวอย่างการใช้งาน:

captureStderr err "./useless.sh"

echo -$err-

มันไม่ใช้ไฟล์ชั่วคราว แต่อย่างน้อยสิ่งที่น่าเกลียดถูกห่อในฟังก์ชั่น


@ShadowWizard สงสัยเล็กน้อยที่ด้านข้างของฉัน ในฝรั่งเศสลำไส้ใหญ่มักจะมีช่องว่างนำหน้า ฉันใช้กฎเดียวกันนี้กับคำตอบภาษาอังกฤษโดยไม่ตั้งใจ หลังจากตรวจสอบสิ่งนี้ฉันรู้ว่าฉันจะไม่ทำผิดพลาดอีกครั้ง
เตฟาน

@ ไชโยสเตฟานนี่ยังถูกพูดถึงที่นี่ด้วย :)
Shadow Wizard คือ Ear For You

1
evalมีวิธีการที่ปลอดภัยในการทำเช่นนี้กว่าการใช้เป็น ตัวอย่างเช่นprintf -v "$1" '%s' "$(<tmpFile)"อย่าเสี่ยงกับการใช้รหัสโดยอำเภอใจหากTMPDIRตัวแปรของคุณถูกตั้งค่าเป็นอันตราย (หรือชื่อตัวแปรปลายทางของคุณมีค่าดังกล่าว)
ชาร์ลส์ดัฟฟี่

1
ในทำนองเดียวกันมีประสิทธิภาพมากขึ้นกว่าrm -- "$tmpFile" rm $tmpFile
ชาร์ลส์ดัฟฟี่

2

นี่เป็นปัญหาที่น่าสนใจที่ฉันหวังว่าจะมีทางออกที่สง่างาม น่าเศร้าที่ฉันจบลงด้วยโซลูชันที่คล้ายกับ Mr. Leffler แต่ฉันจะเพิ่มว่าคุณสามารถโทรหาไร้ประโยชน์จากภายในฟังก์ชั่น Bash เพื่อการอ่านที่ดีขึ้น:

#! / bin / ทุบตี

ฟังก์ชั่นไร้ประโยชน์ {
    /tmp/useless.sh | sed 's / Output / ไร้ประโยชน์ /'
}

ข้อผิดพลาด = $ (ไม่มีประโยชน์)
echo $ ERROR

การเปลี่ยนเส้นทางเอาต์พุตอื่น ๆ ทั้งหมดจะต้องสำรองข้อมูลโดยไฟล์ชั่วคราว


2

POSIX

สามารถจับ STDERR ด้วยเวทมนตร์การเปลี่ยนเส้นทางบางส่วน:

$ { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&3 ; } 2>&1); } 3>&1
lrwxrwxrwx 1 rZZt rZZt 7 Aug 22 15:44 /bin -> usr/bin/

$ echo $error
ls: cannot access '/XXXX': No such file or directory

หมายเหตุท่อ STDOUT ของคำสั่ง (ที่นี่ที่ls) { }จะทำภายในด้านในสุด หากคุณกำลังรันคำสั่งง่าย ๆ (เช่นไม่ใช่ไพพ์) คุณสามารถลบวงเล็บปีกกาข้างในออกได้

คุณไม่สามารถไพพ์นอกคำสั่งได้เนื่องจาก piping สร้าง subshell bashและzshและการกำหนดให้กับตัวแปรใน subshell นั้นจะไม่สามารถใช้ได้กับเชลล์ปัจจุบัน

ทุบตี

ในbashมันจะดีกว่าที่จะไม่สมมติว่า file descriptor 3 ไม่ได้ใช้:

{ error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1; 
exec {tmp}>&-  # With this syntax the FD stays open

zshหมายเหตุว่านี้ไม่ได้ทำงานใน


ขอบคุณคำตอบนี้สำหรับแนวคิดทั่วไป


คุณสามารถอธิบายบรรทัดนี้พร้อมรายละเอียดได้หรือไม่? ไม่เข้าใจ 1> & $ tmp; {error = $ ({{ls -ld / XXXX / bin | tr o Z;} 1> & $ tmp;} 2> & 1); } {tmp}> & 1;
Thiago Conrado

1

โพสต์นี้ช่วยฉันในการหาโซลูชันที่คล้ายกันเพื่อจุดประสงค์ของฉัน:

MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`

จากนั้นตราบใดที่ข้อความของเราไม่ใช่สตริงว่างเราก็ส่งต่อไปยังสิ่งอื่น สิ่งนี้จะแจ้งให้เราทราบว่า format_logs.py ของเราล้มเหลวด้วยข้อยกเว้นของ python


1

จับภาพและพิมพ์ stderr

ERROR=$( ./useless.sh 3>&1 1>&2 2>&3 | tee /dev/fd/2 )

ทำให้พังถล่ม

คุณสามารถใช้$()เพื่อดักจับ stdout แต่คุณต้องการจับ stderr แทน ดังนั้นคุณจะสลับ stdout และ stderr ใช้ fd 3 เป็นที่เก็บข้อมูลชั่วคราวในอัลกอริทึมสลับมาตรฐาน

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

หมายเหตุ: นั่นคือการเปลี่ยนเส้นทางที่น่ากลัวในบรรทัดเดียวและการสั่งซื้อมีความสำคัญ $()จะโลภ stdout ของteeในตอนท้ายของท่อและท่อตัวเองเส้นทาง stdout ของ./useless.shการ stdin ของteeหลังจากที่เราเปลี่ยน stdin และ stdout ./useless.shสำหรับ

ใช้ stdout ของ. / useless.sh

สหกรณ์กล่าวว่าเขายังคงต้องการที่จะใช้ (ไม่เพียง แต่พิมพ์) stdout ./useless.sh | sed 's/Output/Useless/'เช่น

ไม่มีปัญหาเพียงทำมันก่อนที่จะแลกเปลี่ยน stdout และ stderr ฉันขอแนะนำให้ย้ายเข้าไปในฟังก์ชั่นหรือไฟล์ (นอกจากนี้ - useless.sh) และเรียกมันแทน. / useless.sh ในบรรทัดด้านบน

อย่างไรก็ตามหากคุณต้องการจับ stdout และ stderr ฉันคิดว่าคุณต้องถอยกลับไปที่ไฟล์ชั่วคราวเพราะ$()จะทำทีละไฟล์และทำให้ subshell ไม่สามารถส่งคืนตัวแปรได้


1

วนคำตอบของ Tom Hale ซ้ำอีกครั้งฉันพบว่าเป็นไปได้ที่จะห่อโยคะการเปลี่ยนเส้นทางให้เป็นฟังก์ชันเพื่อนำมาใช้ใหม่ได้ง่ายขึ้น ตัวอย่างเช่น:

#!/bin/sh

capture () {
    { captured=$( { { "$@" ; } 1>&3 ; } 2>&1); } 3>&1
}

# Example usage; capturing dialog's output without resorting to temp files
# was what motivated me to search for this particular SO question
capture dialog --menu "Pick one!" 0 0 0 \
        "FOO" "Foo" \
        "BAR" "Bar" \
        "BAZ" "Baz"
choice=$captured

clear; echo $choice

เป็นไปได้ที่จะทำให้เรื่องนี้ง่ายขึ้น ยังไม่ได้ทดสอบอย่างละเอียดโดยเฉพาะ แต่ดูเหมือนจะใช้ได้กับทั้ง bash และ ksh


0

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

$ .useless.sh 2> >( ERROR=$(<) )
-bash: command substitution: line 42: syntax error near unexpected token `)'
-bash: command substitution: line 42: `<)'

จากนั้นฉันก็ลอง

$ ./useless.sh 2> >( ERROR=$( cat <() )  )
This Is Output
$ echo $ERROR   # $ERROR is empty

อย่างไรก็ตาม

$ ./useless.sh 2> >( cat <() > asdf.txt )
This Is Output
$ cat asdf.txt
This Is Error

ดังนั้นเปลี่ยนตัวกระบวนการจะทำโดยทั่วไปสิ่งที่ถูกต้อง ... แต่น่าเสียดายที่เมื่อใดก็ตามที่ผมห่อภายใน STDIN >( )กับสิ่งที่อยู่ในในความพยายามที่จะจับภาพที่ให้กับตัวแปรที่ผมสูญเสียเนื้อหาของ$() $()ฉันคิดว่าเป็นเพราะ$()เปิดตัวกระบวนการย่อยที่ไม่สามารถเข้าถึงไฟล์อธิบายใน / dev / fd ซึ่งเป็นเจ้าของโดยกระบวนการหลัก

การทดแทนกระบวนการทำให้ฉันซื้อความสามารถในการทำงานกับสตรีมข้อมูลซึ่งไม่ได้อยู่ใน STDERR อีกต่อไปโชคไม่ดีที่ฉันดูเหมือนจะไม่สามารถจัดการกับวิธีที่ฉันต้องการ


1
ถ้าคุณไม่แล้วคุณจะเห็นการส่งออกของ./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" ) ERRORปัญหาคือการทดแทนกระบวนการทำงานใน sub-shell ดังนั้นค่าที่ตั้งใน sub-shell จะไม่ส่งผลกระทบต่อ parent shell
Jonathan Leffler

0
$ b=$( ( a=$( (echo stdout;echo stderr >&2) ) ) 2>&1 )
$ echo "a=>$a b=>$b"
a=>stdout b=>stderr

3
ดูเหมือนว่าเป็นความคิดที่ดี แต่สำหรับ Mac OSX 10.8.5 มันจะพิมพ์ออกมาa=> b=>stderr
Heath Borders

3
ฉันเห็นด้วยกับ @HeathBorders; สิ่งนี้ไม่สร้างผลลัพธ์ที่แสดง ปัญหาที่นี่คือการaประเมินและกำหนดใน sub-shell และการกำหนดใน sub-shell ไม่ส่งผลกระทบต่อ parent shell (ทดสอบบน Ubuntu 14.04 LTS และ Mac OS X 10.10.1.)
Jonathan Leffler

เช่นเดียวกันใน Windows GitBash ดังนั้นมันไม่ทำงาน ( GNU bash, version 4.4.12(1)-release (x86_64-pc-msys))
Kirby

ไม่ทำงานSLE 11.4อย่างใดอย่างหนึ่งและสร้างผลกระทบที่อธิบายโดย
@JonathanLeffler

ในขณะที่รหัสนี้อาจตอบคำถาม แต่มีบริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีการที่รหัสนี้ตอบคำถามช่วยปรับปรุงมูลค่าระยะยาว
β.εηοιτ.βε


0

สำหรับข้อผิดพลาดในการพิสูจน์คำสั่งของคุณ:

execute [INVOKING-FUNCTION] [COMMAND]

execute () {
    function="${1}"
    command="${2}"
    error=$(eval "${command}" 2>&1 >"/dev/null")

    if [ ${?} -ne 0 ]; then
        echo "${function}: ${error}"
        exit 1
    fi
}

แรงบันดาลใจในการผลิตแบบลีน:


การแก้ปัญหาคือสำนวน toeput ifมอบหมายภายใน ให้ฉันโพสต์โซลูชันแยกต่างหาก
tripleee


0

การปรับปรุงคำตอบของ YellowApple :

นี่คือฟังก์ชัน Bash เพื่อจับ stderr ลงในตัวแปรใด ๆ

stderr_capture_example.sh:

#!/usr/bin/env bash

# Capture stderr from a command to a variable while maintaining stdout
# @Args:
# $1: The variable name to store the stderr output
# $2: Vararg command and arguments
# @Return:
# The Command's Returnn-Code or 2 if missing arguments
function capture_stderr {
  [ $# -lt 2 ] && return 2
  local stderr="$1"
  shift
  {
    printf -v "$stderr" '%s' "$({ "$@" 1>&3; } 2>&1)"
  } 3>&1
}

# Testing with a call to erroring ls
LANG=C capture_stderr my_stderr ls "$0" ''

printf '\nmy_stderr contains:\n%s' "$my_stderr"

การทดสอบ:

bash stderr_capture_example.sh

เอาท์พุท:

 stderr_capture_example.sh

my_stderr contains:
ls: cannot access '': No such file or directory

ฟังก์ชันนี้สามารถใช้เพื่อจับภาพตัวเลือกที่ส่งคืนของdialogคำสั่ง

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