“ xargs grep” ทำอะไร?


25

ฉันรู้grepคำสั่งและฉันกำลังเรียนรู้เกี่ยวกับฟังก์ชันการใช้งานของxargsดังนั้นฉันอ่านหน้านี้ซึ่งให้ตัวอย่างบางส่วนเกี่ยวกับวิธีการใช้xargsคำสั่ง

ฉันสับสนโดยตัวอย่างล่าสุดตัวอย่างที่ 10 มันบอกว่า "คำสั่ง xargs ดำเนินการคำสั่ง grep เพื่อค้นหาไฟล์ทั้งหมด (ในบรรดาไฟล์ที่มาจากคำสั่ง find) ที่มีสตริง 'stdlib.h'"

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

อย่างไรก็ตามสิ่งที่แตกต่างในการใช้ก็คือ

$ find . -name '*.c' | grep 'stdlib.h'

?

เห็นได้ชัดว่าฉันยังคงดิ้นรนกับสิ่งที่ xargs กำลังทำอยู่ดังนั้นความช่วยเหลือใด ๆ ที่ชื่นชม!


2
คำถามนี้อาจมีประโยชน์: XArgs จำเป็นเมื่อใด
TheOdd

คำตอบ:


30
$ find . -name '*.c' | grep 'stdlib.h'

สิ่งนี้จะทำให้ท่อส่งออก (stdout) * จากfindถึง (stdin ของ) * grep 'stdlib.h' เป็นข้อความ (เช่นชื่อไฟล์จะถือเป็นข้อความ) grepทำสิ่งปกติและค้นหาบรรทัดที่ตรงกันในข้อความนี้ (ชื่อไฟล์ใด ๆ ที่ตัวเองมีรูปแบบ) เนื้อหาของไฟล์ไม่เคยอ่าน

$ find . -name '*.c' | xargs grep 'stdlib.h'

สิ่งนี้สร้างคำสั่ง grep 'stdlib.h'ที่แต่ละผลลัพธ์จากfindการเป็นอาร์กิวเมนต์ - ดังนั้นสิ่งนี้จะมองหาการจับคู่ภายในแต่ละไฟล์ที่พบโดยfind( xargsสามารถคิดว่าเป็นการเปลี่ยน stdin เป็นอาร์กิวเมนต์ไปยังคำสั่งที่กำหนด) *

ใช้-type fในคำสั่งค้นหาของคุณหรือคุณจะได้รับข้อผิดพลาดจากgrepสำหรับไดเรกทอรีที่ตรงกัน นอกจากนี้หากชื่อไฟล์มีช่องว่างxargsจะทำให้เสียอย่างรุนแรงดังนั้นให้ใช้ตัวคั่น null โดยเพิ่ม-print0และxargs -0เพื่อผลลัพธ์ที่เชื่อถือได้มากขึ้น:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* เพิ่มจุดอธิบายเพิ่มเติมเหล่านี้ตามที่แนะนำในความคิดเห็นโดย @cat


2
คุณอาจพิจารณาการสังเกต (เพราะดูเหมือนว่าคุณละเว้น) จุดสำคัญที่|ท่อstdoutไปยังstdinของ grep ซึ่งไม่เหมือนกับอาร์กิวเมนต์ของ grep และให้ผลลัพธ์ที่สับสน
แมว

1
หรือใช้ GNU find -name '*.c' -exec grep stdlib.h {} +หาของ ฉันสวยมากไม่เคยใช้ xargs ไม่มีใครประหลาดใจที่พูดถึงว่า xargs มีจุดประสงค์คล้ายกันในการgrep $(find)สั่งการทดแทนดังนั้นฉันจึงเขียนคำตอบของฉันเอง การอธิบาย xargs เป็นการทดแทนคำสั่งโดยมีข้อ จำกัด และปัญหาน้อยลงดูเหมือนเป็นเรื่องปกติ
ปีเตอร์กอร์เดส

สถานการณ์หนึ่งที่ฉันใช้ xargs บนคือถ้าฉันลบไฟล์จำนวนมากอันเป็นผลมาจากการค้นหา หากคุณเพียงแค่ -exec rm มันจะรัน rm ในแต่ละไฟล์ทีละไฟล์ซึ่งไม่มีประสิทธิภาพมาก การไปที่ xargs จะทำทุกอย่างพร้อมกันในหนึ่ง rm การ จำกัด การพูด -n50 (ทำครั้งละ 50) สามารถป้องกันการล้นบรรทัดคำสั่ง (เกิดปัญหากับไฟล์จำนวนมาก)
lsd

1
@lsd: ทำไมไม่find -deleteใช้กรณีพิเศษล่ะ หรือสำหรับคำสั่งอื่นนอกเหนือจากrmถ้าคุณมี GNU แล้วให้-exec some_command {} +จัดกลุ่มเป็นกลุ่มเช่น xargs แทน\;พฤติกรรมการรันคำสั่งแยกกันสำหรับแต่ละรายการ
Peter Cordes

@lsd findวิ่งคำสั่งในแต่ละไฟล์หากว่าใช้-exec command \;ทั้งคู่xargsและ-exec command \+จะเรียกคำสั่งด้วยจำนวนอาร์กิวเมนต์สูงสุดที่ระบบอนุญาต กล่าวอีกนัยหนึ่งพวกเขาเทียบเท่า
Sergiy Kolodyazhnyy

6

xargs รับอินพุตมาตรฐานและเปลี่ยนเป็นบรรทัดคำสั่ง args

find . -name '*.c' | xargs grep 'stdlib.h' คล้ายกันมากกับ

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

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


แต่ทั้งสองคนนี้ดูดเพราะพวกเขาทำลายถ้าชื่อไฟล์ของคุณมีช่องว่าง แต่ใช้find -print0 | xargs -0งานได้จริง

find . -name '*.c' -exec grep 'stdlib.h' {} +

ที่ไม่เคยทำให้ท่อชื่อไฟล์ใด ๆ : findแบตช์พวกเขาเป็นบรรทัดคำสั่งขนาดใหญ่และทำงานgrepโดยตรง

\;แทนที่จะ+รัน grep แยกกันสำหรับแต่ละไฟล์ซึ่งช้ากว่ามาก อย่าทำอย่างนั้น แต่+เป็นส่วนขยายของ GNU ดังนั้นคุณต้องxargsทำสิ่งนี้อย่างมีประสิทธิภาพหากคุณไม่สามารถหา GNU ได้


ถ้าคุณปล่อยออกxargs, find | grepไม่ตรงกับรูปแบบของตนกับรายการชื่อไฟล์ที่findพิมพ์

ดังนั้น ณ จุดนี้คุณอาจทำเช่นfind -name stdlib.hกัน แน่นอนด้วย-name '*.c' -name stdlib.hคุณจะไม่ได้รับผลลัพธ์ใด ๆ เพราะรูปแบบเหล่านั้นไม่สามารถจับคู่ได้ทั้งคู่และพฤติกรรมเริ่มต้นของ find คือการรวมและกฎเข้าด้วยกัน

ทดแทนlessที่จุดใดก็ได้ในกระบวนการเพื่อดูว่าส่วนใดส่วนหนึ่งของไพพ์ไลน์ที่สร้างขึ้น


อ่านเพิ่มเติม: http://mywiki.wooledge.org/BashFAQมีบางสิ่งที่ยอดเยี่ยม


1
GNU xargs ยัง-dต้องตั้งค่าตัวคั่นดังนั้นคุณสามารถใช้-d'\n'จัดการรายการที่คั่นด้วยบรรทัดใหม่ซึ่งอาจเป็นประโยชน์หากคุณจัดการรายการชื่อไฟล์ในไฟล์ ฯลฯ (ตราบใดที่ชื่อไฟล์ไม่มี ขึ้นบรรทัดใหม่ในพวกเขานั่นคือ.)
ilkkachu

@ilkkachu: ใช่การขึ้นบรรทัดใหม่ในชื่อไฟล์นั้นหายากกว่าการเว้นวรรคเพราะพวกเขาทำลายสคริปต์ส่วนใหญ่ myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $ (ค้นหา) ; }สามารถใช้งานได้เหมือนเดิม หรือในฐานะซับเดี่ยวซับ(IFS=...; cmd...)วูลล์ยังทำงานเพื่อให้การเปลี่ยนแปลงกับ IFS โดยไม่ต้องบันทึก / กู้คืน
Peter Cordes

@PeterCordes โปรดอย่าทำcommand $( find )สิ่งที่ประเภท ชื่อไฟล์ที่มีปัญหาซึ่งมีช่องว่างและตัวอักษรพิเศษสามารถทำลายสิ่งของประเภทนี้ได้ อย่างน้อยที่สุดอัญประกาศคำสั่งทดแทน
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy: ขอบคุณสำหรับการชี้ให้เห็นว่าดูเหมือนว่าฉันแนะนำให้ทำเช่นนั้นจริง ๆ คนที่อ่านข้อมูลไม่ออกอาจมีการคัดลอก / วางซึ่งแทนที่จะอ่านหัวข้อถัดไป อัปเดตไปยังที่อยู่ที่
Peter Cordes

@SergiyKolodyazhnyy: หรือว่าคุณตอบความคิดเห็นของฉัน? ขอให้สังเกตว่าผมตั้งดังนั้นจึงเทียบเท่ากับการใช้IFS xargs '-d\n'การขยายตัวและการประมวลผล Glob metacharacter เปลือกที่เกิดขึ้นก่อนที่จะมีผลกระทบคำสั่งเปลี่ยนตัวดังนั้นฉันคิดว่ามันปลอดภัยแม้จะมีชื่อไฟล์ที่มีหรือ$() >ยอมรับว่าการใช้การแบ่งคำสั่งในการแทนที่คำสั่งไม่ใช่วิธีปฏิบัติที่ดียกเว้นการใช้แบบโต้ตอบครั้งเดียวที่คุณรู้บางอย่างเกี่ยวกับชื่อไฟล์ แต่command "$(find)"จะเป็นประโยชน์เฉพาะในกรณีที่คุณคาดหวังในการผลิตว่า 1 ชื่อไฟล์ ...
ปีเตอร์ Cordes

5

โดยทั่วไปxargsจะใช้สำหรับกรณีที่คุณต้องการไพพ์ (ที่มีสัญลักษณ์|) บางอย่างจากคำสั่งหนึ่งไปยังอีกคำสั่ง ( Command1 | Command2) แต่เอาต์พุตจากคำสั่งแรกไม่ได้รับอย่างถูกต้องเป็นอินพุตสำหรับคำสั่งที่สอง

สิ่งนี้มักจะเกิดขึ้นเมื่อคำสั่งที่สองไม่ได้จัดการกับอินพุตข้อมูลผ่าน Standard In (stdin) อย่างถูกต้อง (เช่น: หลายบรรทัดเป็นอินพุตวิธีการตั้งค่าบรรทัดอักขระที่ใช้เป็นอินพุตพารามิเตอร์หลายตัวเป็นอินพุตชนิดข้อมูลที่ได้รับเป็น อินพุต ฯลฯ .. ) หากต้องการให้ตัวอย่างรวดเร็วให้ทดสอบสิ่งต่อไปนี้:

ตัวอย่างที่ 1:

ls | echo- สิ่งนี้จะไม่ทำอะไรเลยเนื่องจากechoไม่รู้วิธีจัดการอินพุตที่เขาได้รับ ในกรณีนี้ถ้าเราใช้xargsมันจะประมวลผลอินพุตในวิธีที่สามารถจัดการได้อย่างถูกต้องโดยecho(เช่น: เป็นข้อมูลบรรทัดเดียว)

ls | xargs echo- สิ่งนี้จะส่งออกข้อมูลทั้งหมดจากlsในบรรทัดเดียว

ตัวอย่างที่ 2:

สมมติว่าฉันมีไฟล์ goLang หลายไฟล์อยู่ในโฟลเดอร์ที่ชื่อว่า go ฉันจะมองหาพวกเขาด้วยสิ่งนี้:

find go -name *.go -type f | echo- แต่ถ้าสัญลักษณ์ไปป์ตรงนั้นและechoท้ายที่สุดมันจะไม่ทำงาน

find go -name *.go -type f | xargs echo- ที่นี่มันจะทำงานขอบคุณxargsแต่ถ้าฉันต้องการการตอบสนองจากfindคำสั่งในแต่ละบรรทัดเดียวฉันจะทำต่อไปนี้:

find go -name *.go -type f | xargs -0 echo- ในกรณีนี้การส่งออกจากเดิมที่จะได้รับการแสดงโดยfindecho

คำสั่งเช่นcp, echo, rm, lessและอื่น ๆ xargsที่จำเป็นต้องมีวิธีที่ดีกว่าที่จะจัดการกับการป้อนข้อมูลที่ได้รับผลประโยชน์เมื่อใช้กับ


4

xargs ใช้เพื่อสร้างอาร์กิวเมนต์บรรทัดคำสั่งโดยอัตโนมัติ (ตามปกติ) ในรายการไฟล์

ดังนั้นการพิจารณาทางเลือกบางอย่างเพื่อใช้xargsคำสั่งfollowoing :

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

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

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\;จะสร้างหนึ่งgrepกระบวนการสำหรับทุกไฟล์ซึ่งโดยทั่วไปถือว่าเป็นการปฏิบัติที่ไม่ดีและอาจทำให้ระบบมีภาระมากหากพบไฟล์จำนวนมาก
  2. หากมีไฟล์จำนวนมากgrep 'stdlib.h' $(find . -name '*.c')คำสั่งจะล้มเหลวเนื่องจากเอาต์พุตของการ$(...)ดำเนินการจะเกินความยาวบรรทัดคำสั่งสูงสุดของเชลล์

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

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