xargs - ผนวกแต่ละอาร์กิวเมนต์ด้วยพารามิเตอร์


คำตอบ:


13

วิธีหนึ่งในการทำ:

echo "a b c" | xargs printf -- '-f %s\n' | xargs mycommand

นี้อนุมานa, bและcไม่ได้มีช่องว่างการขึ้นบรรทัดใหม่, คำพูดหรือเครื่องหมาย :)

ด้วย GNU findutilคุณสามารถจัดการกรณีทั่วไป แต่มันซับซ้อนกว่าเล็กน้อย:

echo -n "a|b|c" | tr \| \\0 | xargs -0 printf -- '-f\0%s\0' | xargs -0 mycommand

คุณสามารถแทนที่|คั่นด้วยตัวอักษรอื่น ๆ บางอย่างที่ไม่ได้ปรากฏในa, หรือbc

แก้ไข:เนื่องจาก@MichaelMolบันทึกไว้ด้วยรายการอาร์กิวเมนต์ที่ยาวมากจึงมีความเสี่ยงที่จะเกิดความยาวสูงสุดของอาร์กิวเมนต์ที่สามารถส่งผ่านไปmycommandได้ ที่เกิดขึ้นเมื่อที่ผ่านมาxargsจะแบ่งรายการและเรียกใช้สำเนาของผู้อื่นmycommandและมีความเสี่ยงของมันออกจาก -funterminated หากคุณกังวลเกี่ยวกับสถานการณ์นั้นคุณสามารถเปลี่ยนxargs -0ข้างต้นด้วยสิ่งนี้:

... | xargs -x -0 mycommand

วิธีนี้จะไม่แก้ปัญหา แต่จะยกเลิกการทำงานmycommandเมื่อรายการอาร์กิวเมนต์ยาวเกินไป


1
คุณมีความเสี่ยงที่น่าเกลียดเกินกว่าที่ควรARG_MAXและมีการ-fแยกออกจากพารามิเตอร์ที่จับคู่
Michael Mol

@MichaelMol นั่นเป็นจุดที่ดี mycommandแต่ผมไม่คิดว่าจะมีวิธีใดที่มีความหมายในการจัดการกับสถานการณ์ที่โดยไม่ต้องรู้เพิ่มเติมเกี่ยวกับ คุณสามารถเพิ่ม-xเป็นครั้งสุดท้ายxargsได้เสมอ
Satō Katsura

ฉันคิดว่าวิธีการแก้ปัญหาที่เหมาะสมนั้นอาจไม่ได้ใช้xargsเลยและใช้findถ้ามันสามารถใช้ได้ วิธีนี้มีอันตราย อย่างน้อยคุณควรเตือนกรณีที่ล้มเหลวในคำตอบของคุณ
Michael Mol

@MichaelMol ฉันไม่เห็นเลยว่าfindจะเป็นคำตอบทั่วไปที่ดีกว่าได้อย่างไรโดยเฉพาะอย่างยิ่งเมื่ออาร์กิวเมนต์เริ่มต้นไม่ใช่ชื่อไฟล์ :)
Satō Katsura

เราไม่รู้ว่าอะไรคือข้อโต้แย้งเริ่มต้น; เราเห็นเพียงตัวอย่างที่กำหนดไม่ใช่สถานการณ์ที่เป็นแรงบันดาลใจให้กับคำถาม สัญชาตญาณแสดงให้เห็นว่ามีการโต้แย้งชื่อ-fและด้วยเครื่องมือตัวอย่างที่lsใช้สำหรับภาพประกอบ @ not-a-user กำลังจัดการกับชื่อไฟล์ และได้รับfindข้อเสนอ-execอาร์กิวเมนต์ซึ่งช่วยให้คุณสร้างบรรทัดคำสั่งก็ไม่เป็นไร (ตราบใดที่mycommandได้รับอนุญาตให้ดำเนินการมากกว่าหนึ่งครั้งถ้าไม่ใช่เรามีปัญหาอีกอย่างกับการใช้xargsที่นี่ ... )
Michael Mol

5

วิธีที่ดีกว่าในการจัดการกับมัน (IMO) คือ:

  • ในzsh:

    l=(a b c)
    mycommand -f$^l
    

    หรือใช้การซิปอาร์เรย์เพื่อให้อาร์กิวเมนต์ไม่ถูกแนบกับตัวเลือก:

    l=(a b c) o=(-f)
    mycommand "${o:^^l}"

    ด้วยวิธีนี้จะยังคงทำงานได้หากlอาร์เรย์มีองค์ประกอบว่างเปล่าหรือองค์ประกอบที่มีช่องว่างหรืออักขระที่มีปัญหาอื่น ๆxargsๆ ตัวอย่าง:

    $ l=(a '' '"' 'x y' c) o=(-f)
    $ printf '<%s>\n' "${o:^^l}"
    <-f>
    <a>
    <-f>
    <>
    <-f>
    <">
    <-f>
    <x y>
    <-f>
    <c>
  • ใน rc:

    l=(a b c)
    mycommand -f$l
  • ใน fish:

    set l a b c
    mycommand -f$l

(AFAIK rcและfishไม่มีการซิปอาร์เรย์)

ด้วยเชลล์แบบเหมือน Bourne แบบเก่าbashคุณสามารถทำได้ตลอดเวลา (ยังคงอนุญาตให้ตัวละครใด ๆ ในองค์ประกอบของ$@อาร์เรย์):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"

1
อีกวิธีในการใช้ bash คือการใช้ตัวแปรอาเรย์ที่มีชื่อ for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK ถ้าสิ่งนี้เร็วกว่า แต่การผนวก 2 องค์ประกอบเข้ากับอาร์เรย์ดูเหมือนว่าควรเป็น O (n) ในขณะที่setลูปของคุณอาจคัดลอกและแยกวิเคราะห์รายการ ARG ที่สะสมใหม่ทุกครั้ง (O (n ^ 2))
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.