ไฟล์ grep จากรายการ


14

ฉันกำลังพยายามเรียกใช้ grep กับรายการของไฟล์สองสามร้อย:

$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php

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

$ grep -i 'foo' <(cat files.txt)

$ grep -i 'foo' admin.php
The foo was found

ฉันคุ้นเคยกับการ-fตั้งค่าสถานะซึ่งจะอ่านรูปแบบจากไฟล์ แต่จะอ่านไฟล์อินพุตได้อย่างไร?

ฉันได้พิจารณาวิธีแก้ปัญหาที่น่ากลัวของการคัดลอกไฟล์ไปยังไดเรกทอรีชั่วคราวตามที่cpดูเหมือนว่าจะสนับสนุน<(cat files.txt)รูปแบบและจากนั้นมีการ grepping ไฟล์ เชอร์ลี่ย์มีวิธีที่ดีกว่า

คำตอบ:


22

ดูเหมือนว่าคุณกำลัง grepping รายชื่อไฟล์ไม่ใช่ตัวไฟล์เอง <(cat files.txt)เพียงแค่แสดงรายการไฟล์ ลอง<(cat $(cat files.txt))เชื่อมมันเข้าด้วยกันจริงๆแล้วค้นหามันเป็นสตรีมเดียวหรือ

grep -i 'foo' $(cat files.txt)

เพื่อให้ grep ไฟล์ทั้งหมด

อย่างไรก็ตามหากมีไฟล์มากเกินไปในรายการคุณอาจมีปัญหากับจำนวนอาร์กิวเมนต์ ในกรณีนี้ฉันแค่เขียน

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt

ขอขอบคุณ! ฉันไม่ทราบว่าwhileสามารถรับบรรทัด file.txt ได้
dotancohen

คุณจะต้องการปิดการใช้งานส่วนglobของผู้ประกอบการ+ แยก globที่นี่ (ยกเว้นกรณีที่เปลือกเป็น zsh)
Stéphane Chazelas

1
whileไม่ได้รับบรรทัดจากไฟล์อย่างแน่นอนreadกำลังทำอยู่ whileเพียงแค่ให้เราทำแบบวนซ้ำ ลูปจะสิ้นสุดลงเมื่อreadล้มเหลว (เช่นส่งคืนโค้ดส่งคืนที่ไม่เป็นศูนย์) โดยปกติเนื่องจากถึงจุดสิ้นสุดไฟล์
PM 2Ring

1
หากต้องการอ่าน (ข้อความ) สายไวยากรณ์คือIFS= read -r filename, read filenameเป็นอย่างอื่น
Stéphane Chazelas

1
โปรดทราบว่า-Hเป็นส่วนขยายของ GNU --คุณกำลังขาดหายไปบางส่วน
Stéphane Chazelas

8
xargs grep -i -- foo /dev/null < files.txt

สมมติว่าไฟล์นั้นว่างเปล่าหรือขึ้นบรรทัดใหม่ (ซึ่งสามารถใช้เครื่องหมายคำพูดหรือแบ็กสแลชเพื่อหลีกเลี่ยงตัวคั่นเหล่านั้น) ด้วย GNU xargsคุณสามารถระบุตัวคั่นด้วย-d(ซึ่งจะปิดการใช้งานการจัดการข้อความ)

(unset -v IFS; set -f; grep -i -- foo $(cat files.txt))

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

-เหล่านั้นยังถือว่าไม่มีไฟล์ที่ถูกเรียกว่า


มันจะดีกว่า / เร็วขึ้นเพื่อใช้$(< file)แทน$(cat file)อย่างน้อยในและbash zsh
jimmij

7

หากต้องการอ่านรายชื่อไฟล์จาก stdin xargsคุณสามารถใช้ เช่น,

cat files.txt | xargs -d'\n' grep -i -- 'foo'

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

FWIW วิธีการนี้ค่อนข้างเร็วกว่าwhile readลูปเนื่องจากreadคำสั่งของ bash ช้ามาก - มันอ่านตัวอักขระข้อมูลตามตัวอักษรในขณะที่xargsอ่านอินพุตได้อย่างมีประสิทธิภาพ นอกจากนี้ให้xargsเรียกใช้grepคำสั่งหลาย ๆ ครั้งเท่าที่จำเป็นเท่านั้นโดยแต่ละการเรียกใช้จะได้รับชื่อไฟล์หลายชื่อและมีประสิทธิภาพมากกว่าการเรียกใช้grepทีละชื่อสำหรับแต่ละชื่อไฟล์

ดูหน้า man xargsและหน้าข้อมูล xargs สำหรับรายละเอียดเพิ่มเติม


3

xargsสามารถอ่านรายการจากไฟล์ (เช่นfiles.txtรายการของคุณ) ด้วยตัวเลือกมัน:

   --arg-file=file
   -a file
          Read items from file instead of standard input.  If you use this
          option, stdin remains unchanged when commands are  run.   Other
          wise, stdin is redirected from /dev/null.

ดังนั้นควรทำงานด้วย:

xargs -a files.txt grep -i 'foo'

หรือช่องว่างในชื่อไฟล์

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}

1

คุณยังสามารถทำเพื่อ แต่ตัวอย่างของ Orion นั้นง่ายที่สุด:

for i in $(cat files.txt); do grep -i 'foo' $i ; done

(สำหรับแต่ละไฟล์ที่อยู่ใน files.txt ให้เรียกใช้คำสั่ง grep)

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