จำเป็นต้องใช้ xargs เมื่อใด


134

xargsคำสั่งมักจะสร้างความสับสนให้ฉัน มีกฎทั่วไปหรือไม่

ลองพิจารณาสองตัวอย่างด้านล่าง:

$ \ls | grep Cases | less

พิมพ์ไฟล์ที่ตรงกับ 'กรณี' แต่การเปลี่ยนคำสั่งtouchจะต้องxargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

คำตอบ:


143

ความแตกต่างคือข้อมูลที่โปรแกรมเป้าหมายยอมรับ

หากคุณเพียงแค่ใช้ไพพ์ก็จะได้รับข้อมูลใน STDIN (สตรีมอินพุตมาตรฐาน) เป็นกองข้อมูลดิบที่สามารถเรียงลำดับได้ทีละหนึ่งบรรทัด อย่างไรก็ตามบางโปรแกรมไม่ยอมรับคำสั่งของพวกเขาตามมาตรฐานพวกเขาคาดว่ามันจะสะกดคำในข้อโต้แย้งของคำสั่ง ตัวอย่างเช่นใช้ชื่อไฟล์ที่เป็นพารามิเตอร์ในบรรทัดคำสั่งเช่นดังนั้น:touchtouch file1.txt

หากคุณมีโปรแกรมที่ผลชื่อไฟล์บนออกมาตรฐานและต้องการที่จะใช้พวกเขาเป็นข้อโต้แย้งที่จะtouchคุณจะต้องใช้xargsที่อ่านข้อมูลสตรีม STDIN และแปลงแต่ละบรรทัดลงในช่องว่างข้อโต้แย้งแยกออกคำสั่ง

สองสิ่งนี้เทียบเท่ากัน:

# touch file1.txt
# echo file1.txt | xargs touch

อย่าใช้xargsจนกว่าคุณจะรู้ว่ามันกำลังทำอะไรอยู่และทำไมมันถึงต้องการ บ่อยครั้งที่มีวิธีที่ดีกว่าในการทำงานมากกว่าใช้xargsเพื่อบังคับให้เกิดการแปลง กระบวนการแปลงยังเต็มไปด้วยข้อผิดพลาดที่อาจเกิดขึ้นเช่นการหลบหนีและการขยายคำ


2
คำเตือนให้ความรู้สึกเล็กน้อยกับฉัน จากสองตัวเลือกทั่วไปในการรับสตรีมเข้าสู่บรรทัดคำสั่ง ( xargsและ$(...)) xargs นั้นปลอดภัยกว่าการทดแทนคำสั่ง และฉันไม่สามารถจำได้ว่าเคยเจอชื่อไฟล์ที่ถูกกฎหมายกับการขึ้นบรรทัดใหม่ในนั้น การหลบหนีและการขยายคำไม่เป็นปัญหากับการทดแทนคำสั่งไม่ใช่ xargs หรือไม่
camh

6
@camh: พวกเขามีข้อผิดพลาดที่อาจเกิดขึ้นกับทั้งสอง ในเปลือกคุณต้องกังวลเกี่ยวกับชื่อไฟล์ที่ได้รับการแบ่งในช่องว่างแท็บและขึ้นบรรทัดใหม่ ใน xargs คุณต้องกังวลกับการขึ้นบรรทัดใหม่เท่านั้น ใน xargs หากคุณมีรูปแบบผลลัพธ์ที่ถูกต้องคุณสามารถแยกคำ / ชื่อไฟล์ด้วยอักขระ NUL แทน ( xargs -0) ซึ่งมีประโยชน์ในการเชื่อมfind -print0ต่อ
Ken Bloom

ไม่xargsเรียกโปรแกรมผ่านเปลือกที่มีพื้นที่แยก args, หรือไม่ก็จริงสร้างรายการอาร์กิวเมนต์ภายใน (เช่น. สำหรับใช้กับexecv/ execp)?
detly

1
มันสร้างขึ้นภายในและใช้ execvp ดังนั้นจึงปลอดภัย นอกจากนี้ GNU xargs (ที่ใช้บน Linux และอื่น ๆ อีกสองสามอย่าง) ช่วยให้คุณสามารถกำหนดบรรทัดใหม่เป็นตัวคั่นของคุณ-d \nแม้ว่า BSD xargs (OSX et al) ไม่ปรากฏขึ้นเพื่อรองรับตัวเลือกนี้
ปุย

72

หากต้องการขยายคำตอบที่ได้ให้ไปแล้วxargsสามารถทำสิ่งหนึ่งที่น่าสนใจที่สำคัญมากขึ้นเรื่อย ๆ ในแนวการคำนวณแบบกระจายและแบบกระจายในปัจจุบัน: มันสามารถประมวลผลงานแบบขนาน

ตัวอย่างเช่น:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

จะเข้ารหัส * .wav => * .flac โดยใช้สามกระบวนการพร้อมกัน ( -P 3)


ว้าว. ฉันควรจะรู้เรื่องนี้เมื่อสัปดาห์ที่แล้วเมื่อฉันทำสิ่งเดียวกัน (ยกเว้นการใช้ OGG) กับ 50GiB ของ WAV :)
Alois Mahdal

ทำไมไม่ใช้พารามิเตอร์ -exec ที่ค้นหามี
Evgeny

3
@Evgeny -execพารามิเตอร์จะไม่ประมวลผลงานแบบขนาน
แอมเฟตามาจิน

ดีที่จะต้องทราบว่า-0ข้อโต้แย้งที่จะxargsทำให้มันพิจารณาNULLตัวละครที่จะเป็นตัวคั่นรายการอินพุต find -print0เอาท์พุทรายการที่คั่นด้วย NULL นี่เป็นวิธีปฏิบัติที่ดีสำหรับชื่อไฟล์ที่อาจมีช่องว่างเครื่องหมายคำพูดหรืออักขระพิเศษอื่น ๆ
Dan Dascalescu

24

xargs มีประโยชน์อย่างยิ่งเมื่อคุณมีรายการพา ธ ของไฟล์บน stdin และต้องการทำบางสิ่งกับมัน ตัวอย่างเช่น:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

ลองตรวจสอบทีละขั้นตอนนี้:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

กล่าวอีกนัยหนึ่งอินพุตของเราคือรายการเส้นทางที่เราต้องการทำบางสิ่ง

หากต้องการทราบว่า xargs ทำอะไรกับพา ธ เหล่านี้เคล็ดลับที่ดีคือการเพิ่มechoก่อนคำสั่งของคุณเช่น:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

-n 1อาร์กิวเมนต์จะทำให้ xargs เปิดแต่ละบรรทัดลงในคำสั่งของตัวเอง sed -i "s/color/colour/g"คำสั่งจะแทนที่เกิดขึ้นทั้งหมดcolorด้วยcolourสำหรับไฟล์ที่ระบุ

โปรดทราบว่าวิธีนี้ใช้ได้เฉพาะในกรณีที่คุณไม่มีช่องว่างในเส้นทางของคุณ หากคุณทำคุณควรใช้เส้นทางที่สิ้นสุดด้วยค่า null เป็นอินพุตไปยัง xargs โดยผ่าน-0แฟล็ก ตัวอย่างการใช้จะเป็น:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

ซึ่งทำเช่นเดียวกับที่เราอธิบายไว้ข้างต้น แต่ยังทำงานได้หากหนึ่งในเส้นทางนั้นมีช่องว่างอยู่

นี้ทำงานร่วมกับคำสั่งใด ๆ ที่ก่อให้ชื่อไฟล์เป็นผลผลิตเช่นหรือfind locateหากคุณใช้งานมันในที่เก็บ git ที่มีไฟล์จำนวนมากมันอาจจะมีประสิทธิภาพมากกว่าที่จะใช้มันgit grep -lแทนgit ls-filesเช่น:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

git grep -l "color" "*.tex"คำสั่งจะให้รายการ "* .tex" ไฟล์ที่มีวลีที่ว่า "สี" แล้ว


1
ทรู แต่ถ้าคุณได้เรียนรู้นี้คุณควรจะยังเรียนรู้ทำไมวนลูปกับการส่งออกปฏิบัติไม่ดีหาหรือไม่?
Wildcard

6

อาร์กิวเมนต์แรกของคุณแสดงให้เห็นถึงความแตกต่างค่อนข้างดี

\ls | grep Cases | lessช่วยให้คุณสามารถเรียกดูรายชื่อไฟล์ที่ผลิตโดยและls grepไม่สำคัญว่าพวกเขาจะเป็นชื่อไฟล์ แต่เป็นเพียงข้อความบางส่วน

\ls | grep Cases | xargs lessให้คุณเรียกดูไฟล์ที่ชื่อถูกสร้างขึ้นโดยส่วนแรกของคำสั่ง xargsใช้เวลารายการของชื่อไฟล์เป็น input และคำสั่งในบรรทัดคำสั่งของตนและทำงานคำสั่งที่มีชื่อไฟล์บนของบรรทัดคำสั่ง

เมื่อพิจารณาที่จะใช้xargsเก็บไว้ในใจว่ามันคาดว่าการจัดรูปแบบการป้อนข้อมูลในทางที่แปลก: ช่องว่างที่คั่นด้วย\, 'และ"ใช้สำหรับ quoting (ในลักษณะผิดปกติเพราะ\ไม่ได้เป็นคำพูดภายในพิเศษ) ใช้เฉพาะถ้าคุณชื่อไฟล์ของคุณไม่ได้มีช่องว่างหรือxargs\'"


@Gilles: xargsมี-0, --nullตัวเลือกในการแก้ไขปัญหาช่องว่าง (เป็นไปได้อย่างมากว่าฉันได้เรียนรู้จากคุณ :) ดังนั้นฉันคิดว่าคุณกำลังอ้างถึงการโทรแบบไม่มีตัวเลือกxargแต่ฉันสับสนโดยอ้างอิงจากคำพูด คุณมีลิงค์หรือตัวอย่างที่เกี่ยวข้องหรือไม่? .. (ps. | xargs lessเป็น "เคล็ดลับ" ที่มีประโยชน์ +1 .. ขอบคุณ ..
Peter.O

4

ในตัวอย่างของคุณคุณไม่จำเป็นต้องใช้xargsเลยเพราะfindจะทำสิ่งที่คุณต้องการ

สิ่งที่คุณต้องการใช้findคือ:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

ในตัวอย่างนี้-maxdepth 1หมายถึงค้นหาเฉพาะในไดเรกทอรีปัจจุบันอย่าลงไปในไดเรกทอรีย่อยใด ๆ โดยค่าเริ่มต้นการค้นหาจะดูในไดเรกทอรีย่อยทั้งหมด (ซึ่งมักเป็นสิ่งที่คุณต้องการ) เว้นแต่คุณจะ จำกัด ด้วย maxdepth {}เป็นชื่อของแฟ้มที่จะได้รับการทดแทนในสถานที่และเป็นหนึ่งในสองเครื่องหมายสิ้นสุดของคำสั่งที่เป็นอยู่อื่น+;ความแตกต่างระหว่างพวกเขาคือนั่น;หมายถึง exec คำสั่งในแต่ละไฟล์หนึ่งครั้งในขณะที่+หมายถึง exec คำสั่งในไฟล์ทั้งหมดในครั้งเดียว แต่โปรดทราบว่าเปลือกของคุณอาจจะพยายามที่จะแปลความหมายของ;ตัวเองเพื่อให้คุณจะต้องหนีมันด้วยหรือ\; ';'ใช่findมีจำนวนเล็กน้อยที่น่ารำคาญเช่นนี้ แต่พลังของมันมากกว่าทำให้มัน

ทั้งสองfindและxargsมีความยุ่งยากในการเรียนรู้ในตอนแรก เพื่อช่วยให้คุณเรียนรู้xargsลองใช้-pหรือ--interactiveตัวเลือกที่จะแสดงคำสั่งมันจะดำเนินการและแจ้งให้คุณว่าคุณต้องการเรียกใช้หรือไม่

ในทำนองเดียวกันกับfindที่คุณสามารถใช้-okในสถานที่ของ-execที่จะแจ้งให้คุณหรือไม่ว่าคุณต้องการที่จะเรียกใช้คำสั่ง

มีบางครั้งที่เมื่อfindไม่สามารถทำทุกอย่างที่คุณต้องการและนั่นคือที่xargsมา-execคำสั่งจะยอมรับเพียงหนึ่งอินสแตนซ์ของการ{}ปรากฏดังนั้นถ้าคุณจะได้รับข้อผิดพลาดด้วยfind -type f -exec cp {} {}.bak \;ดังนั้นคุณสามารถทำเช่นนั้นได้ :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการเรียกใช้คำสั่งในคู่มือ GNU Findutils

นอกจากนี้ฉันกล่าวว่าfindสิ่งที่คุณต้องการอย่างปลอดภัยเพราะเมื่อคุณกำลังจัดการกับไฟล์คุณจะพบช่องว่างและตัวละครอื่น ๆ ที่จะทำให้เกิดปัญหากับxargsถ้าคุณใช้-0หรือ--nullตัวเลือกพร้อมกับสิ่งที่สร้างรายการอินพุตสิ้นสุดด้วยอักขระ null แทน ของช่องว่าง



@Wildcard ชื่อไฟล์ที่มีช่องว่างหรือตัวอักษรเช่น'หรือ"อาจเป็นปัญหาในขณะที่findจะจัดการกับกรณีเหล่านั้นโดยไม่มีปัญหา
aculich

ใช่ฉันรู้. ดูคำตอบของฉันคำถามที่เชื่อมโยง ฉันอาจจะต้องใช้ถ้อยคำใหม่ในคำถามข้างต้นหรือเพิ่มวลี "ดูคำถาม ... " ข้างหน้า : D
Wildcard

1

xargs(พร้อมด้วยfind, sort, du, uniq, perlและคนอื่น ๆ ไม่กี่) ยอมรับสวิตช์บรรทัดคำสั่งจะพูดว่า "STDIN มีรายชื่อของไฟล์ที่แยกจากกันโดย NUL (0x00) ไบต์" ทำให้ง่ายต่อการจัดการชื่อไฟล์ด้วยช่องว่างและตัวละครตลกอื่น ๆ ชื่อไฟล์ไม่มี NUL


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