วิธีลบ \ n ระหว่างเอาต์พุตของคำสั่ง echo สองคำสั่ง?


13

ฉันมีไฟล์ข้อความที่มีชื่อไฟล์หนึ่งชื่อในแต่ละบรรทัด:

111_c4l5r120.png
123_c4l4r60.png
135_c4l4r180.png
147_c4l3r60.png
15_c4l1r120.png
...

ฉันต้องการแปลงเป็นรูปนี้:

111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
...

ใช้รหัสนี้:

#!/bin/bash
while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" >> output.txt   
   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

แต่ผลลัพธ์คือ:

111_c4l5r120.png 
111
123_c4l4r60.png 
123
135_c4l4r180.png 
135
147_c4l3r60.png 
147
15_c4l1r120.png 
15
...

ฉันจะเปลี่ยนสคริปต์เพื่อให้ได้ผลลัพธ์ตามที่ต้องการได้อย่างไร


Google พบผลลัพธ์ที่ดีเช่นนี้
โทมัส Dickey

คำตอบ:


17

ถ้าคุณไม่มีความต้องการที่จะใช้เชลล์สำหรับคำตอบนี้Terdonให้ทางเลือกที่ดีกว่า

เนื่องจากคุณกำลังใช้bash(ตามที่ระบุไว้ใน shebang ของสคริปต์) คุณสามารถใช้-nตัวเลือกเพื่อ echo:

echo -n "${line} " >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

หรือคุณสามารถใช้คุณสมบัติเชลล์เพื่อประมวลผลบรรทัดโดยไม่ต้องใช้cut:

echo "${line} ${line%%_*}" >> output.txt

(แทนที่ทั้งสองechoบรรทัด)

อีกทางหนึ่ง, printfจะทำเคล็ดลับด้วย, ทำงานในPOSIX เชลล์ใด ๆ, และโดยทั่วไปจะดีกว่า (ดูที่เหตุใด printf จึงดีกว่า echo?สำหรับรายละเอียด):

printf "%s " "${line}" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

หรือ

printf "%s %s\n" "${line}" "${line%%_*}" >> output.txt

(พูดอย่างเคร่งครัดในที่ราบ/bin/sh, echo -nไม่ได้เป็นแบบพกพา . เนื่องจากคุณอย่างชัดเจนโดยใช้bashมันตกลงที่นี่.)


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
terdon

23

อย่าทำสิ่งนี้ในเปลือก! มันซับซ้อนเกินความจำเป็นมีแนวโน้มที่จะเกิดข้อผิดพลาดและห่างไกลช้าลง มีเครื่องมือมากมายที่ออกแบบมาเพื่อจัดการกับข้อความดังกล่าว ตัวอย่างเช่นในsed(ที่นี่สมมติว่าการใช้งาน GNU หรือ BSD ล่าสุดสำหรับ-E):

$ sed -E 's/([^_]*).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

หรือสำหรับใด ๆsed:

$ sed 's/\([^_]*\).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

Perl:

$ perl -pe 's/(.+?)_.*/$& $1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

awk:

$ awk -F_ '{print $0,$1}' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

1
แม้ว่าสาธารณูปโภคภายนอกจะไม่ค่อยดีนัก
EKons

6
@ ΈρικΚωνσταντόπουλοςใช่พวกเขาเป็น หลายคำสั่งของขนาดเร็วขึ้นจริง เปลือกมันไม่ค่อยดีเท่านี้ งานหลักของเชลล์กำลังเรียกใช้งานยูทิลิตี้ภายนอกหลังจากทั้งหมด เปรียบเทียบเวลาที่ใช้โดยแนวทางของ OP กับที่ดำเนินการโดยโซลูชันใด ๆ ที่นี่ เชลล์ลูปนั้นช้ามาก หากคุณต้องการความน่าเชื่อถือมากขึ้นอ่านนี้
terdon

ในแง่ของการพกพา, ไม่ ในแง่ของความเร็วใช่ นอกจากนี้ @ StéphaneChazelasเป็นชื่อแทนของคุณหรือไม่
EKons

4
@ ΈρικΚωνσταντόπουλοςΘα 'θελα :) ไม่เขาเพิ่งจะเขียนคำตอบที่ยอดเยี่ยม 2 คำตอบที่เกี่ยวข้องกับหัวข้อความคิดเห็นทั้งสอง สำหรับการพกพาด้วยข้อยกเว้น (เล็กน้อย) ของวิธี perl ซึ่งจะทำงานกับบางสิ่งบางอย่างเช่น ~ 90% ของเครื่อง * nix โซลูชันทั้งสามนี้เป็นแบบพกพาและไม่เชื่อเรื่องพระเจ้าเชลล์ หรือตกลงคุณสามารถทำให้เป็นsedหนึ่งในsed 's/\([^_]*\).*/& \1/' fileการพกพาพิเศษ ประเด็นคือคุณสามารถไว้ใจawkและsedอยู่ที่นั่นได้มากกว่าที่คุณจะไว้ใจได้ในเรื่องอื่น
terdon

2

อยู่นี่ไง:

#!/bin/bash

while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" `echo "$line" | cut -d'_' -f 1` >> output.txt
#   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

เอาท์พุท:

$ rm -rf output.txt
$ ./test.sh 1.1; cat output.txt
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.