วิธีการใช้คำสั่งเชลล์กับแต่ละบรรทัดของเอาต์พุตคำสั่ง


192

สมมติว่าฉันมีเอาต์พุตบางส่วนจากคำสั่ง (เช่นls -1):

a
b
c
d
e
...

ฉันต้องการที่จะใช้คำสั่ง (พูดecho) กับแต่ละคนในทางกลับกัน เช่น

echo a
echo b
echo c
echo d
echo e
...

เป็นวิธีที่ง่ายที่สุดในการทุบตีอะไร


3
ls -1อาจจะเป็นตัวอย่างที่นี่ lsแต่มันเป็นสิ่งสำคัญที่ต้องจำไว้ว่ามันไม่ดีที่จะแยกการส่งออกของ ดู: mywiki.wooledge.org/ParsingLs
codeforester

คำตอบ:


217

xargsมันอาจจะง่ายที่สุดในการใช้งาน ในกรณีของคุณ:

ls -1 | xargs -L1 echo

-Lมั่นใจธงการป้อนข้อมูลที่อ่านได้อย่างถูกต้อง จากหน้าคนของxargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]


5
@Dennis ดูเหมือนไม่เหมือน: ls | xargs -L2 echoและls -1 | xargs -L2 echoให้ผลลัพธ์ที่แตกต่างกันสองรายการ อดีตถูกทั้งหมดในบรรทัดเดียว
Alex Budovski

2
@Alex: ฉันได้รับผลลัพธ์เดียวกัน
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

6
xargsสามารถเรียกใช้ไฟล์ที่ปฏิบัติการได้เท่านั้นซึ่งไม่ใช่ฟังก์ชั่นของเชลล์หรือคำสั่งในตัวของเชลล์ สำหรับอดีตทางออกที่ดีที่สุดน่าจะเป็นวิธีที่มีreadการวนซ้ำ
pabouk

12
ฉันหวังว่าคำตอบนี้จะรวมคำอธิบายของสิ่งที่-L1มีไว้สำหรับ
Wyck

164

คุณสามารถใช้การดำเนินการเสริมพื้นฐานในแต่ละบรรทัด:

ls -1 | while read line ; do echo $line ; done

หรือคุณสามารถไพพ์เอาท์พุทเป็น sed เพื่อการทำงานที่ซับซ้อนมากขึ้น:

ls -1 | sed 's/^\(.*\)$/echo \1/'

1
ดูเหมือนว่าคำสั่ง sed จะไม่ทำงาน: sh: cho: not found a sh: cho: not foundดูเหมือนว่าeจะเป็นการใช้คำสั่ง in เพื่อเป็นคำสั่ง sed หรืออะไรบางอย่าง
Alex Budovski

+1 สำหรับwhileลูป cmd1 | while read line; do cmd2 $line; done. หรือwhile read line; do cmd2 $line; done < <(cmd1)สิ่งที่ไม่ได้สร้าง subshell นี่เป็นเวอร์ชันที่เรียบง่ายของsedคำสั่งของคุณ:sed 's/.*/echo &/'
หยุดชั่วคราวจนกว่าจะมีประกาศเพิ่มเติม

1
@Alex: เปลี่ยนเครื่องหมายคำพูดคู่เป็นคำพูดเดี่ยว
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

5
อ้างอิงการ"$line"วนรอบในขณะเพื่อหลีกเลี่ยงการแยกคำ
Ignis

3
ลองใช้read -r lineเพื่อป้องกันการreadสับสนกับตัวละครที่หลบหนี ตัวอย่างเช่นecho '"a \"nested\" quote"' | while read line; do echo "$line"; doneให้"a "nested" quote"ซึ่งสูญเสียการหลบหนี ถ้าเราทำecho '"a \"nested\" quote"' | while read -r line; do echo "$line"; doneเราจะได้รับ"a \"nested\" quote"ตามที่คาดไว้ ดูwiki.bash-hackers.org/commands/builtin/read
Warbo

9

คุณสามารถใช้สำหรับการวนรอบ :

สำหรับไฟล์ใน *; ทำ
   echo "$ file"
เสร็จแล้ว

โปรดทราบว่าหากคำสั่งที่เป็นปัญหายอมรับหลายอาร์กิวเมนต์การใช้xargsนั้นเกือบจะมีประสิทธิภาพมากกว่าเสมอเพราะจะต้องวางยูทิลิตีที่มีปัญหาไว้หนึ่งครั้งแทนที่จะเป็นหลาย ๆ ครั้ง


1
เป็นมูลค่าการอธิบายการใช้ xargs ที่เหมาะสม / ปลอดภัยเช่น printf '%s\0' * | xargs -0 ...- มิฉะนั้นก็ค่อนข้างไม่ปลอดภัยกับชื่อไฟล์ด้วยช่องว่าง, คำพูด, ฯลฯ
ชาร์ลส์ดัฟฟี่

8

คุณสามารถใช้ Sed เพื่อทำได้โดยที่มันเป็น GNU sed

... | sed 's/match/command \0/e'

มันทำงานอย่างไร:

  1. ทดแทนการจับคู่กับการจับคู่คำสั่ง
  2. ในการทดแทนคำสั่งรัน
  3. แทนที่บรรทัดที่แทนที่ด้วยเอาต์พุตคำสั่ง

น่าอัศจรรย์ ฉันลืมที่จะนำ คำสั่งeในตอนท้าย แต่หลังจากนั้นก็เห็นการตอบกลับของคุณและมันทำงานได้ ฉันพยายามที่จะผนวก ID แบบสุ่มระหว่าง 1,000 ถึง 15,000 เมื่อ SED ตรงกับบรรทัด cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi

6
for s in `cmd`; do echo $s; done

หาก cmd มีเอาต์พุตขนาดใหญ่:

cmd | xargs -L1 echo

7
หากcmdมีช่องว่างในเอาต์พุตช่องแรกจะล้มเหลว
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป


2

xargs ล้มเหลวด้วยเครื่องหมายแบ็กสแลชเครื่องหมายคำพูด มันต้องเป็นอะไรซักอย่าง

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

ตัวเลือก xargs -0:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1ยุติรายการด้วยอักขระขึ้นบรรทัดใหม่ดังนั้นtrแปลเป็นอักขระ null

วิธีนี้ช้ากว่าการวนซ้ำด้วยตนเองประมาณ 50 เท่า for ... (ดูคำตอบของMichael Aaron Safyan ) (3.55s เทียบกับ 0.066s) แต่สำหรับคำสั่งอินพุตอื่น ๆ เช่นค้นหาค้นหาอ่านจากไฟล์ ( tr \\n \\0 <file) หรือคล้ายกันคุณต้องทำงานกับxargsสิ่งนี้


1

ผลลัพธ์ที่ดีกว่าสำหรับฉัน:

ls -1 | xargs -L1 -d "\n" CMD

ดีกว่า แต่ไม่สมบูรณ์แบบ find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandจะจัดการกรณีที่ผลลัพธ์ของls -1ไม่ชัดเจน ใช้-printf '%P\0'แทน-print0ถ้าคุณไม่ต้องการนำหน้า./ในแต่ละอัน
Charles Duffy

1

ฉันชอบที่จะใช้เพ่งพิศเพื่อใช้หลายคำสั่งในรายการเช่น

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

อย่างไรก็ตามการหลบหนีของตัวละครที่หลบหลีกได้จะมีขนเล็กน้อย

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