ดูเหมือนว่าฉันมีคำตอบที่มีอยู่ทั้งหมดในหน้านี้ผิดรวมถึงคำตอบที่ถูกต้อง นั่นเกิดจากความจริงที่ว่าคำถามนั้นเป็นคำที่คลุมเครือ
สรุป: หากคุณต้องการรันคำสั่ง"หนึ่งครั้งสำหรับแต่ละบรรทัดอินพุตที่กำหนด"ผ่านทั้งบรรทัด (โดยไม่ต้องขึ้นบรรทัดใหม่) ให้กับคำสั่งเป็นอาร์กิวเมนต์เดียวดังนั้นนี่เป็นวิธีที่ดีที่สุดในการเข้ากันได้กับ UNIX:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
อาจมีหรือไม่มีส่วนขยายที่มีประโยชน์ซึ่งอนุญาตให้คุณทำไปได้tr
แต่ไม่มีใน OS X และระบบ UNIX อื่น ๆ
ตอนนี้สำหรับคำอธิบายที่ยาว ...
มีสองประเด็นที่ต้องคำนึงถึงเมื่อใช้ xargs:
- มันแยกอินพุตเป็น "อาร์กิวเมนต์" ได้อย่างไร; และ
- จำนวนอาร์กิวเมนต์ที่ต้องส่งคำสั่งย่อยในแต่ละครั้ง
ในการทดสอบพฤติกรรมของ xargs เราต้องการยูทิลิตี้ที่แสดงจำนวนครั้งที่มันถูกเรียกใช้งานและด้วยจำนวนอาร์กิวเมนต์ ฉันไม่รู้ว่ามียูทิลิตี้มาตรฐานในการทำเช่นนั้นหรือไม่ แต่เราสามารถเขียนโค้ดได้อย่างง่ายดายในการทุบตี:
#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
สมมติว่าคุณบันทึกไว้show
ในไดเรกทอรีปัจจุบันของคุณและทำให้มันทำงานได้นี่คือวิธีการทำงาน:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
ตอนนี้ถ้าคำถามเดิมเป็นจริงเกี่ยวกับประเด็นที่ 2 ด้านบน (อย่างที่ฉันคิดว่าเป็นหลังจากอ่านไปสองสามครั้ง) และจะต้องอ่านดังนี้ (เปลี่ยนเป็นตัวหนา):
ฉันจะทำให้ xargs เรียกใช้งานคำสั่งเพียงครั้งเดียวสำหรับแต่ละอาร์กิวเมนต์ของอินพุตที่กำหนดได้อย่างไร พฤติกรรมเริ่มต้นของมันคือการป้อนข้อมูลเข้าไปในข้อโต้แย้งและดำเนินการคำสั่งให้น้อยที่สุดเท่าที่เป็นไปได้ผ่านหลายอาร์กิวเมนต์ไปยังแต่ละอินสแตนซ์
-n 1
แล้วคำตอบคือ
ลองเปรียบเทียบพฤติกรรมเริ่มต้นของ xargs ซึ่งแยกอินพุตรอบช่องว่างและเรียกคำสั่งสองสามครั้งเท่าที่จะทำได้
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
และพฤติกรรมกับ-n 1
:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
หากในทางกลับกันคำถามเดิมเป็นเรื่องเกี่ยวกับประเด็นที่ 1 การแยกอินพุตและต้องอ่านแบบนี้ (หลายคนที่มาที่นี่ดูเหมือนจะคิดว่าเป็นกรณีหรือสับสนทั้งสองประเด็น):
ฉันจะทำให้ xargs รันคำสั่งกับว่าอาร์กิวเมนต์หนึ่งสำหรับแต่ละบรรทัดของการป้อนข้อมูลให้? พฤติกรรมเริ่มต้นของมันคือการก้อนเส้นรอบช่องว่าง
ดังนั้นคำตอบนั้นลึกซึ้งยิ่งขึ้น
ใคร ๆ ก็คิดว่า-L 1
อาจเป็นประโยชน์ได้ แต่กลับกลายเป็นว่าไม่ได้เปลี่ยนการแยกวิเคราะห์ มันจะรันคำสั่งเพียงครั้งเดียวสำหรับแต่ละบรรทัดอินพุตโดยมีอาร์กิวเมนต์มากเท่าที่มีบนบรรทัดอินพุตนั้น:
$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
ไม่เพียงแค่นั้น แต่หากบรรทัดลงท้ายด้วยช่องว่างมันจะผนวกเข้ากับถัดไป:
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
เห็นได้ชัดว่า-L
ไม่เกี่ยวกับการเปลี่ยนวิธีที่ xargs แยกอินพุตเป็นอาร์กิวเมนต์
อาร์กิวเมนต์เดียวที่ทำในรูปแบบข้ามแพลตฟอร์ม (ยกเว้นส่วนขยาย GNU) คือ-0
ซึ่งแยกอินพุตรอบ ๆ NUL ไบต์
จากนั้นเป็นเพียงการแปลบรรทัดใหม่เป็น NUL ด้วยความช่วยเหลือของtr
:
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
ตอนนี้การแยกวิเคราะห์อาร์กิวเมนต์ดูทั้งหมดรวมทั้งช่องว่างต่อท้าย
สุดท้ายถ้าคุณรวมเทคนิคนี้เข้าด้วย-n 1
กันคุณจะได้รับคำสั่งหนึ่งคำสั่งต่อหนึ่งบรรทัดอินพุตไม่ว่าคุณจะใส่อะไรก็ตามซึ่งอาจเป็นอีกวิธีหนึ่งในการดูคำถามเดิม (อาจเป็นคำถามที่เข้าใจง่ายที่สุด)
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
find /path -type f -delete
จะยิ่งมีประสิทธิภาพมากขึ้น :)