การจับเอาท์พุทหลายบรรทัดในตัวแปร Bash


583

ฉันมีสคริปต์ 'myscript' ที่แสดงผลลัพธ์ต่อไปนี้:

abc
def
ghi

ในสคริปต์อื่นฉันเรียก:

declare RESULT=$(./myscript)

และ$RESULTได้รับความคุ้มค่า

abc def ghi

มีวิธีในการเก็บผลลัพธ์ด้วยการขึ้นบรรทัดใหม่หรือด้วยอักขระ '\ n' เพื่อให้ฉันสามารถแสดงผลด้วย ' echo -e' ได้หรือไม่


1
มันทำให้ฉันประหลาดใจ คุณไม่มี $ (cat ./myscipt) ใช่ไหม มิฉะนั้นฉันจะคาดหวังว่ามันจะพยายามรันคำสั่ง abc, def และ ghi
Johannes Schaub - litb

@litb: ใช่ฉันคิดอย่างนั้น คุณยังสามารถใช้ $ (<./ myscript) ซึ่งหลีกเลี่ยงการเรียกใช้คำสั่ง
Jonathan Leffler

2
(NB: ความคิดเห็นสองข้อด้านบนอ้างถึงการแก้ไขคำถามที่เริ่มขึ้นฉันมีสคริปต์ 'myscript' ที่มีสิ่งต่อไปนี้ซึ่งนำไปสู่คำถามการแก้ไขคำถามปัจจุบัน ( ฉันมีสคริปต์) myscript 'ที่แสดงผลต่อไปนี้ ) แสดงความคิดเห็นที่ไม่จำเป็นอย่างไรก็ตามการแก้ไขจาก 2011-11-11 นานหลังจากที่ทั้งสองแสดงความคิดเห็นแล้ว
Jonathan Leffler

คำตอบ:


1081

ที่จริงแล้วผลลัพธ์มีสิ่งที่คุณต้องการ - เพื่อแสดงให้เห็น:

echo "$RESULT"

สิ่งที่คุณแสดงคือสิ่งที่คุณได้รับจาก:

echo $RESULT

ตามที่ระบุไว้ในความคิดเห็นความแตกต่างคือ (1) รุ่นที่ยกมาสองครั้งของตัวแปร ( echo "$RESULT") รักษาระยะห่างภายในของค่าตรงตามที่มันจะแสดงในตัวแปร - ขึ้นบรรทัดใหม่แท็บช่องว่างหลายและทั้งหมด - ในขณะที่ (2) ) เวอร์ชันที่ไม่มีเครื่องหมาย ( echo $RESULT) แทนที่แต่ละลำดับของช่องว่างหนึ่งแท็บและขึ้นบรรทัดใหม่ด้วยช่องว่างเดียว ดังนั้น (1) เก็บรักษารูปร่างของตัวแปรอินพุตในขณะที่ (2) สร้างบรรทัดเดี่ยวที่มีความยาวที่อาจยาวมากโดยมี 'word' คั่นด้วยช่องว่างเดี่ยว (โดยที่ 'word' เป็นลำดับของอักขระที่ไม่ใช่ช่องว่าง; ไม่ต้องเป็นตัวอักษรและตัวเลขใด ๆ ในคำใด ๆ )


69
@troelskn: ความแตกต่างคือ (1) รุ่นที่ยกมาสองเท่าของตัวแปรรักษาระยะห่างภายในของค่าตามที่มันจะถูกแสดงในตัวแปร, การขึ้นบรรทัดใหม่, แท็บ, ช่องว่างหลายและทั้งหมดในขณะที่ (2) รุ่นแทนที่ แต่ละลำดับของช่องว่างหนึ่งแท็บและขึ้นบรรทัดใหม่ด้วยช่องว่างเดียว ดังนั้น (1) เก็บรักษารูปร่างของตัวแปรอินพุตในขณะที่ (2) สร้างบรรทัดเดี่ยวที่มีความยาวที่อาจยาวมากโดยมี 'word' คั่นด้วยช่องว่างเดี่ยว (โดยที่ 'word' เป็นลำดับของอักขระที่ไม่ใช่ช่องว่าง; ไม่ต้องเป็นตัวอักษรและตัวเลขใด ๆ ในคำใด ๆ )
Jonathan Leffler

23
เพื่อให้เข้าใจง่ายขึ้นคำตอบ: คำตอบบอกว่า echo "$ RESULT" รักษาบรรทัดใหม่ในขณะที่ echo $ RESULT ไม่ทำเช่นนั้น
Yu Shen

สิ่งนี้ล้มเหลวในการรักษาบรรทัดใหม่และช่องว่างนำในบางสถานการณ์
CommaToast

3
@CommaToast: คุณจะทำอย่างละเอียดหรือไม่? การขึ้นบรรทัดใหม่จะหายไป ไม่มีวิธีง่าย ๆ ในการทำเช่นนั้น ช่องว่างชั้นนำ - ฉันไม่ได้ตระหนักถึงสถานการณ์ใด ๆ ที่พวกเขากำลังหลงทางอยู่
Jonathan Leffler


90

ข้อผิดพลาดอีกประการหนึ่งคือการแทนที่คำสั่ง - $()- ตัดการขึ้นบรรทัดใหม่ อาจจะไม่สำคัญเสมอ แต่ถ้าคุณอยากที่จะรักษาว่าสิ่งที่ออกคุณจะต้องใช้สายอื่นและบางข้อความ:

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

สิ่งนี้มีความสำคัญอย่างยิ่งหากคุณต้องการจัดการชื่อไฟล์ที่เป็นไปได้ทั้งหมด (เพื่อหลีกเลี่ยงพฤติกรรมที่ไม่ได้กำหนดเช่นการทำงานกับไฟล์ผิด)


4
ฉันต้องทำงานเป็นระยะเวลาหนึ่งด้วยเชลล์ที่แตกที่ไม่ได้ลบบรรทัดใหม่ล่าสุดออกจากการทดแทนคำสั่ง ( ไม่ใช่ การทดแทนกระบวนการ ) และมันก็หักเกือบทุกอย่าง ตัวอย่างเช่นถ้าคุณทำpwd=`pwd`; ls $pwd/$fileคุณจะได้รับการขึ้นบรรทัดใหม่ก่อนหน้า/และการใส่ชื่อในเครื่องหมายคำพูดคู่นั้นไม่ได้ช่วยอะไรเลย มันได้รับการแก้ไขอย่างรวดเร็ว นี่คือย้อนกลับไปในกรอบเวลา 1983-5 ของ ICL Perq PNX เชลล์ไม่มี$PWDตัวแปรในตัว
Jonathan Leffler

19

ในกรณีที่คุณสนใจในบรรทัดที่เจาะจงให้ใช้อาร์เรย์ผลลัพธ์:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"

3
หากมีช่องว่างในบรรทัดสิ่งนี้จะนับฟิลด์ (เนื้อหาระหว่างช่องว่าง) มากกว่าบรรทัด
เลียม

2
คุณจะใช้และประมวลผลการเปลี่ยนตัวแทนแทนคำสั่ง:readarray readarray -t RESULT < <(./myscript>
chepner

15

นอกเหนือจากคำตอบที่ได้รับจาก @ l0b0 ฉันเพิ่งมีสถานการณ์ที่ฉันต้องการให้ทั้งคู่ขึ้นบรรทัดใหม่ที่ส่งออกโดยสคริปต์และตรวจสอบรหัสส่งคืนของสคริปต์ และปัญหาของคำตอบของ l0b0 คือ 'echo x' กำลังรีเซ็ต $ หรือไม่ กลับไปที่ศูนย์ ... ดังนั้นฉันจัดการเพื่อหาวิธีแก้ปัญหาที่ฉลาดมาก

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"

น่ารักจริง ๆ ฉันมีกรณีการใช้งานที่ฉันต้องการมีdie ()ฟังก์ชั่นที่ยอมรับรหัสออกโดยพลการและข้อความเป็นทางเลือกและฉันต้องการใช้usage ()ฟังก์ชั่นอื่นในการส่งข้อความ แต่บรรทัดใหม่ยังคงถูกบีบอัดไว้ หลีกเลี่ยงสิ่งนั้น
dragon788

1

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

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

แฮ็คด่วนเพื่อให้ดำเนินการตามที่ร้องขอ:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

หมายเหตุสิ่งนี้เพิ่มบรรทัดพิเศษ หากคุณทำงานกับมันคุณสามารถเขียนโปรแกรมได้ฉันขี้เกียจเกินไป


แก้ไข: ในขณะที่กรณีนี้ทำงานได้อย่างสมบูรณ์แบบคนที่อ่านข้อความนี้ควรระวังว่าคุณสามารถบีบ stdin ของคุณได้อย่างง่ายดายในขณะที่ลูปดังนั้นให้สคริปต์ที่จะรันหนึ่งบรรทัดล้าง stdin และออก อย่างที่ฉันคิดว่าจะทำอย่างงั้นเหรอ? ฉันเพิ่งเห็นมันเมื่อเร็ว ๆ นี้ตัวอย่างโค้ดอื่น ๆ ที่นี่: /unix/24260/reading-lines-from-a-file-with-bash-for-vs-while

อีกที! เวลานี้ด้วย filehandle อื่น (stdin, stdout, stderr เท่ากับ 0-2 ดังนั้นเราสามารถใช้ & 3 หรือสูงกว่าในการทุบตี)

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

คุณสามารถใช้ mktemp ได้ แต่นี่เป็นเพียงตัวอย่างโค้ดด่วน การใช้งานสำหรับ mktemp ดูเหมือนว่า:

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

จากนั้นใช้ $ filenamevar เหมือนกับชื่อไฟล์จริง อาจไม่จำเป็นต้องอธิบายที่นี่ แต่มีคนบ่นในความคิดเห็น


ฉันลองใช้วิธีแก้ไขปัญหาอื่น ๆ เช่นกันด้วยข้อเสนอแนะแรกของคุณฉันก็ใช้งานสคริปต์ได้ในที่สุด
Kar.ma

1
Downvote: นี่ซับซ้อนเกินไปและไม่สามารถหลีกเลี่ยงbashหลุมพรางที่พบบ่อยได้
tripleee

คุณมีคนบอกฉันเกี่ยวกับปัญหาไฟล์ stdin แปลก ๆ เมื่อวันก่อนและฉันก็เหมือน "ว้าว" Lemme เพิ่มบางสิ่งบางอย่างได้อย่างรวดเร็วจริงๆ
user1279741

ใน Bash คุณสามารถใช้readส่วนขยายของคำสั่ง-u 3เพื่ออ่านจากไฟล์ descriptor 3
Jonathan Leffler

ทำไมไม่ทำในขณะที่ ... ทำ ... เสร็จแล้ว <<(. myscript.sh)
jtgd

0

วิธีการเกี่ยวกับเรื่องนี้มันจะอ่านแต่ละบรรทัดไปยังตัวแปรและสามารถนำมาใช้ในภายหลัง! say myscript output ถูกเปลี่ยนเส้นทางไปยังไฟล์ชื่อ myscript_output

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'

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