ดำเนินการคำสั่งหนึ่งครั้งต่อหนึ่งบรรทัดอินพุตที่ไพพ์หรือไม่?


162

ls | grep pattern -ฉันต้องการที่จะเรียกใช้คำสั่งภาษาจาวาละครั้งสำหรับการแข่งขันของทุก ในกรณีนี้ฉันคิดว่าฉันสามารถทำได้find pattern -exec java MyProg '{}' \;แต่ฉันอยากรู้เกี่ยวกับกรณีทั่วไป - มีวิธีที่ง่ายที่จะพูดว่า "เรียกใช้คำสั่งหนึ่งครั้งสำหรับอินพุตมาตรฐานทุกบรรทัด" หรือไม่ (ในปลาหรือทุบตี)

คำตอบ:


91

นั่นคือสิ่งที่xargsทำ

... | xargs command

25
ไม่มาก จะให้ผลผลิตprintf "foo bar\nbaz bat" | xargs echo whee whee foo bar baz batอาจจะเพิ่ม-Lหรือ-nตัวเลือก?
Jander

3
@ Jander คำถามค่อนข้างทั่วไปดังนั้นฉันจึงให้เครื่องมือทั่วไป จริงคุณจะต้องปรับพฤติกรรมด้วยตัวเลือกขึ้นอยู่กับสถานการณ์ที่เฉพาะเจาะจง
Keith

4
... | tr '\ n' '\ 0' | xargs -0
vrdhn

7
เช่น "สถานการณ์เฉพาะที่ให้คำตอบที่ถูกต้องกับคำถาม" :)
mattdm

7
หากคุณต้องการดูวิธีที่เหมาะสมในการทำเช่นนี้กับ xargs ดูคำตอบของฉันด้านล่าง
Michael Goldshteyn

167

คำตอบที่ได้รับการยอมรับมีความคิดที่ถูกต้อง แต่ที่สำคัญคือการผ่านสวิทช์ซึ่งหมายความว่า "ดำเนินการคำสั่งครั้งต่อบรรทัดของการส่งออก"xargs-n1

cat file... | xargs -n1 command

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

<file xargs -n1 command

1
สิ่งที่น่าสนใจคือความสามารถในxargsการไม่เรียกใช้หากstdinว่างเปล่า --no-run-if-empty -r:: หากอินพุตมาตรฐานไม่มีช่องว่างใด ๆ ห้ามรันคำสั่ง โดยปกติคำสั่งจะรันหนึ่งครั้งแม้ว่าจะไม่มีอินพุต ตัวเลือกนี้เป็นส่วนขยายของ GNU
Ronan Jouchet

4
คุณเข้าถึงสายภายในได้commandอย่างไร?
BT

นี่คือการใช้ xargs ที่ถูกต้อง หากไม่มี -n1 จะทำงานเฉพาะกับคำสั่งที่จัดการรายการของพารามิเตอร์เป็นการเรียกใช้หลายรายการซึ่งไม่ได้ทำทั้งหมด
masterxilo

3
printf "foo bar \ nbaz bat" | xargs -n1 echo whee แยกด้วยคำพูดไม่ใช่ตามเส้น
Gismo Ranas

112

ใน Bash หรือเชลล์สไตล์ Bourne อื่น ๆ (ash, ksh, zsh, …):

while read -r line; do command "$line"; done

read -rอ่านบรรทัดเดียวจากอินพุตมาตรฐาน ( readโดยไม่ต้อง-rแปลเครื่องหมายแบ็กสแลชคุณไม่ต้องการ) ดังนั้นคุณสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้:

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file

6
เมื่อฉันลองtail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; doneมันไม่ทำงาน มันทำงานกับไฟล์ที่ถูกสอดเข้าไปในwhileลูป, ทำงานกับ Just tail -f, ทำงานกับ just grep, แต่ไม่ใช่กับไพพ์ทั้งสอง ให้ตัวเลือกที่ทำให้มันทำงานgrep--line-buffered

วิธีนี้ใช้ได้เมื่อแต่ละบรรทัดต้องถูกส่งไปยัง stdin:command | while read -r line; do echo "$line" | command ; done
ชัด

21

ฉันเห็นด้วยกับ Keith xargs เป็นเครื่องมือทั่วไปที่สุดสำหรับงาน

ฉันมักจะใช้วิธีการ 3 ขั้นตอน

  • ทำสิ่งพื้นฐานจนกว่าคุณจะมีสิ่งที่คุณต้องการทำงานด้วย
  • เตรียมบรรทัดด้วย awk เพื่อรับไวยากรณ์ที่ถูกต้อง
  • จากนั้นให้ xargs ดำเนินการบางทีด้วยความช่วยเหลือของการทุบตี

มีวิธีที่เล็กกว่าและเร็วกว่า แต่วิธีนี้ใช้ได้ผลเกือบทุกครั้ง

ตัวอย่างง่ายๆ:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

2 บรรทัดแรกเลือกไฟล์ที่จะทำงานจากนั้น awk เตรียมสตริงที่ดีพร้อมกับคำสั่งเพื่อดำเนินการและบางอาร์กิวเมนต์และ $ 1 คืออินพุตคอลัมน์แรกจากไพพ์ และในที่สุดฉันก็แน่ใจว่า xargs ส่งสตริงนี้เพื่อทุบตีที่เพิ่งรันมัน

มันค่อนข้างมากไปหน่อย แต่สูตรนี้ช่วยฉันได้หลายที่เพราะมันยืดหยุ่นมาก


6
หมายเหตุxargs -0ใช้ไบต์ว่างเป็นตัวคั่นเร็กคอร์ดดังนั้นคำสั่งการพิมพ์ awk ของคุณควรเป็นprintf("MyJavaProg --args \"%s\"\0",$1)
glenn jackman

@glenn: ไม่ได้รับถ่าน null จะอัปเดตคำตอบ
Johan

@Johan ไม่ใช่เรื่องใหญ่ แต่ถ้าคุณกำลังใช้awkคุณสามารถมีได้ทำตรงกับรูปแบบและข้ามgrep เช่นls | awk '/xls/ {print...
เอริค Renouf

15

GNU Parallel ถูกสร้างขึ้นสำหรับงานประเภทนั้น การใช้งานที่ง่ายที่สุดคือ:

cat stuff | grep pattern | parallel java MyProg

ดูวิดีโอแนะนำเพื่อเรียนรู้เพิ่มเติม: http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
ไม่มีความต้องการที่แท้จริงสำหรับที่catนี่เนื่องจากgrepสามารถอ่านไฟล์ได้โดยตรง
Eric Renouf


1
ขอบคุณสำหรับลิงค์ฉันไม่จำเป็นต้องยอมรับว่าอ่านได้ง่ายขึ้น แต่ยินดีที่ได้ทราบว่ามีการพิจารณาโดยไม่คำนึงถึง ตอนนี้ฉันจะพูดคลุมเครือเล็กน้อยว่าลิงก์ใช้ไม่ได้จริงเนื่องจากทางเลือกไม่ใช่จริง ๆ< stuff grep patternแต่grep pattern stuffไม่มีการเปลี่ยนเส้นทางหรือไม่ต้องการแมวเลย แต่ถึงกระนั้นก็ไม่ได้เปลี่ยนอาร์กิวเมนต์ของคุณอย่างมีนัยสำคัญและถ้าคุณคิดว่ามันชัดเจนกว่าที่จะใช้สิ่งต่าง ๆ ในท่อที่เริ่มต้นด้วยcatแล้วพลังให้คุณ
Eric Renouf

8

นอกจากนี้ยังมีwhile readห่วงในเปลือกปลา (ฉันคิดว่าคุณต้องการเปลือกปลาพิจารณาว่าคุณใช้แท็ก )

command | while read line
    command $line
end

คะแนนน้อยที่ควรทราบ

  • readไม่-rโต้แย้งและมันแปลว่าแบ็กสแลชของคุณเพื่อให้การใช้งานทั่วไปเป็นเรื่องง่าย
  • คุณไม่จำเป็นต้องพูด$lineเพราะแตกต่างจากทุบตีปลาไม่ได้แยกตัวแปรตามช่องว่าง
  • commandโดยตัวมันเองเป็นข้อผิดพลาดทางไวยากรณ์ (เพื่อตรวจจับการใช้ตัวยึดตำแหน่ง) แทนที่ด้วยคำสั่งจริง

ไม่whileจำเป็นต้องจับคู่กับdo& doneแทนที่จะเป็นend?
AFF

@aff นี่เป็นเรื่องเกี่ยวกับ fish shell โดยเฉพาะซึ่งมีไวยากรณ์ต่างกัน
Konrad Borowski

อานั่นคือความหมายของปลา
aff

6

xargs -I{}หากคุณจำเป็นต้องควบคุมที่ว่าข้อโต้แย้งการป้อนข้อมูลที่ถูกแทรกลงในบรรทัดคำสั่งคุณหรือถ้าคุณจำเป็นต้องทำซ้ำหลายครั้งแล้วคุณจะต้องใช้

ตัวอย่าง # 1

สร้างโครงสร้างโฟลเดอร์เปล่าในanother_folderที่สะท้อนโฟลเดอร์ย่อยในไดเรกทอรีปัจจุบัน:

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
ตัวอย่าง # 2

ใช้การดำเนินการกับรายการไฟล์ที่มาจาก stdin ในกรณีนี้ให้ทำสำเนาของแต่ละ.htmlไฟล์โดยต่อท้าย.bakส่วนขยาย:

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

จากxargsหน้า man สำหรับ MacOS / BSD :

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

xargsหน้าลินุกซ์:

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.

1

เมื่อต้องจัดการกับอินพุตที่ไม่ได้รับอนุญาตฉันชอบที่จะเห็นงานทั้งหมดที่ 'สะกดออก' ทีละบรรทัดสำหรับการตรวจสอบด้วยภาพก่อนที่ฉันจะเรียกใช้ (โดยเฉพาะอย่างยิ่งเมื่อมันเป็นสิ่งที่ทำลายล้าง

ดังนั้นสิ่งที่ฉันทำคือสร้างรายการของพารามิเตอร์ (เช่น. ชื่อผู้ใช้), ฟีดมันไปยังไฟล์ในรูปแบบหนึ่งบันทึกต่อบรรทัดเช่นนี้

johndoe  
jamessmith  
janebrown  

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

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

วิธีนี้หาก regex ของคุณไม่สมบูรณ์คุณจะเห็นว่าคำสั่งใดจะมีปัญหาที่อาจเกิดขึ้น (เช่น/bin/rm -fr johnnyo connor) วิธีนี้คุณสามารถยกเลิกการ regex ของคุณและลองอีกครั้งด้วยรุ่นที่เชื่อถือได้มากขึ้น ชื่อ mangling มีชื่อเสียงสำหรับเรื่องนี้เพราะมันยากที่จะดูแลทุกกรณีขอบเช่น Van Gogh, O'Connors, เซนต์แคลร์, Smith-Wesson

การมีset hlsearchประโยชน์สำหรับการทำเช่นนี้vimเนื่องจากมันจะเน้นการแข่งขันทั้งหมดดังนั้นคุณสามารถมองเห็นได้อย่างง่ายดายหากมันไม่ตรงหรือตรงกับในลักษณะที่ไม่ได้ตั้งใจ

เมื่อ regex ของคุณสมบูรณ์แบบและจับทุกกรณีที่คุณสามารถทดสอบ / คิดแล้วฉันมักจะแปลงเป็นนิพจน์ sed เพื่อให้อัตโนมัติโดยสมบูรณ์สำหรับการทำงานอื่น

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


0

หากโปรแกรมละเว้นท่อ /dev/stdinแต่ยอมรับไฟล์เป็นข้อโต้แย้งแล้วคุณก็สามารถชี้ไปยังแฟ้มพิเศษ

ฉันไม่คุ้นเคยกับ java แต่นี่เป็นตัวอย่างของวิธีที่คุณทำเพื่อทุบตี:

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

$ เป็นสิ่งจำเป็นสำหรับการทุบตีเพื่อแปล\nเป็นบรรทัดใหม่ ฉันไม่แน่ใจว่าทำไม


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