Recursive grep vs find / -type f -exec grep {} \; ข้อใดมีประสิทธิภาพมากขึ้น / เร็วขึ้น?


70

ไฟล์ใดที่มีประสิทธิภาพมากกว่าสำหรับการค้นหาไฟล์ในระบบไฟล์ทั้งหมดที่มีสตริง: grep แบบเรียกซ้ำหรือค้นหาด้วย grep ในคำสั่ง exec? ฉันคิดว่าการค้นหาจะมีประสิทธิภาพมากขึ้นเพราะอย่างน้อยคุณสามารถทำการกรองบางอย่างได้ถ้าคุณรู้จักนามสกุลไฟล์หรือ regex ที่ตรงกับชื่อไฟล์ แต่เมื่อคุณรู้ว่าตัว-type fไหนดีกว่า greu GNU 2.6.3; find (GNU findutils) 4.4.2

ตัวอย่าง:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;


1
ประสิทธิภาพของคณิตศาสตร์ / วิทยาการคอมพิวเตอร์ / อัลกอริทึม
Gregg Leventhal

ตรวจสอบอันนี้ แม้ว่าจะไม่เรียกซ้ำก็จะให้ความเข้าใจที่ดีขึ้น unix.stackexchange.com/questions/47983/…
Ramesh

8
@AvinashRaj เขาไม่ได้ขอความเห็น เขากำลังถามว่าใครจะมีประสิทธิภาพมากขึ้นและ / หรือเร็วกว่าไม่ใช่คนที่ดีกว่า นี่เป็นคำถามที่ตอบได้อย่างสมบูรณ์ซึ่งมีเพียงคำตอบเดียวที่ขึ้นอยู่กับว่าโปรแกรมสองโปรแกรมนี้ทำงานอย่างไรและขึ้นอยู่กับสิ่งที่คุณให้พวกเขาค้นหา
terdon

2
โปรดทราบว่ารูปแบบจะทำงาน้อยลงดังนั้นควรจะเร็วกว่า-exec {} + -exec {} \;คุณอาจต้องเพิ่ม-H(หรือ-h) grepตัวเลือกเพื่อให้ได้ผลลัพธ์ที่เทียบเท่า
มิเคล

คุณอาจไม่ต้องการ-rตัวเลือกgrepสำหรับสิ่งที่สอง
qwertzguy

คำตอบ:


85

ฉันไม่แน่ใจ:

grep -r -i 'the brown dog' /*

เป็นสิ่งที่คุณหมายถึงจริงๆ นั่นหมายถึง grep ซ้ำในไฟล์และ dirs ที่ไม่ได้ซ่อนไว้/(แต่ก็ยังดูในไฟล์และ dirs ที่ซ่อนอยู่ในนั้น)

สมมติว่าคุณหมายถึง:

grep -r -i 'the brown dog' /

สิ่งที่ควรทราบ:

  • grepการปรับใช้ไม่สนับสนุน-rทั้งหมด และในบรรดาพฤติกรรมนั้นแตกต่างกัน: บางคนติดตาม symlinks ไปยังไดเรกทอรีเมื่อเข้าไปในแผนผังไดเรกทอรี (ซึ่งหมายความว่าคุณอาจมองหลาย ๆ ครั้งในไฟล์เดียวกันหรือแม้กระทั่งทำงานในลูปไม่ จำกัด ) บางคนจะไม่ บางตัวจะดูในไฟล์อุปกรณ์ (และใช้เวลาค่อนข้างนาน/dev/zero) หรือไพพ์หรือไฟล์ไบนารี่ ... บางไฟล์จะไม่
  • มันมีประสิทธิภาพตั้งแต่grepเริ่มมองหาไฟล์ภายในทันทีที่ค้นพบมัน แต่ในขณะที่ดูในไฟล์จะไม่ค้นหาไฟล์เพิ่มเติมเพื่อค้นหาอีกต่อไป (ซึ่งอาจเป็นในกรณีส่วนใหญ่)

ของคุณ:

find / -type f -exec grep -i 'the brown dog' {} \;

(ลบสิ่ง-rที่ไม่สมเหตุสมผลตรงนี้ออก) จะไม่มีประสิทธิภาพมากเพราะคุณใช้งานgrepไฟล์หนึ่งไฟล์ ;ควรใช้สำหรับคำสั่งที่ยอมรับอาร์กิวเมนต์เดียวเท่านั้น ยิ่งกว่านั้นที่นี่เนื่องจากgrepดูในไฟล์เดียวมันจะไม่พิมพ์ชื่อไฟล์ดังนั้นคุณจะไม่ทราบว่าการจับคู่นั้นอยู่ที่ไหน

คุณไม่ได้มองภายในแฟ้มอุปกรณ์ท่อ symlinks ... คุณไม่ได้ดังต่อไปนี้ symlinks /proc/memแต่คุณยังคงอาจมองสิ่งที่อยู่ภายในเช่น

find / -type f -exec grep -i 'the brown dog' {} +

จะดีขึ้นมากเพราะgrepคำสั่งน้อยที่สุดเท่าที่จะทำได้ คุณจะได้รับชื่อไฟล์ยกเว้นว่าการเรียกใช้ครั้งสุดท้ายมีเพียงไฟล์เดียว เพื่อที่จะดีกว่าที่จะใช้:

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

หรือกับ GNU grep:

find / -type f -exec grep -Hi 'the brown dog' {} +

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

ด้วยเครื่องมือ GNU:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

ข้างต้นgrepจะมีการเรียกใช้อินสแตนซ์น้อยที่สุดเท่าที่จะเป็นไปได้ แต่findจะดำเนินการค้นหาไฟล์เพิ่มเติมในขณะที่การgrepเรียกใช้ครั้งแรกนั้นอยู่ภายในแบทช์แรก นั่นอาจจะใช่หรือไม่ใช่ก็ได้ ตัวอย่างเช่นเมื่อข้อมูลที่เก็บไว้ในฮาร์ดไดรฟ์แบบหมุนได้findและการgrepเข้าถึงข้อมูลที่เก็บไว้ในสถานที่ต่าง ๆ บนดิสก์จะทำให้ปริมาณงานของดิสก์ช้าลงโดยทำให้หัวดิสก์เคลื่อนที่ตลอดเวลา ในการตั้งค่า RAID (ที่ไหนfindและgrepอาจเข้าถึงดิสก์ที่แตกต่างกัน) หรือบน SSD ซึ่งอาจสร้างความแตกต่างในเชิงบวก

ในการตั้งค่า RAID การเรียกใช้การเรียกใช้หลายรายการพร้อมกัน grepอาจช่วยปรับปรุงสิ่งต่างๆ ยังคงมีเครื่องมือ GNU ในการจัดเก็บ RAID1 กับ 3 ดิสก์

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

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

นอกจากนี้โปรดทราบว่าหากคุณเปลี่ยนเส้นทางxargsเอาต์พุตไปยังอุปกรณ์อื่นนอกจากอุปกรณ์เทอร์มินัลแล้วgrepss จะเริ่มบัฟเฟอร์เอาต์พุตของพวกเขาซึ่งหมายความว่าเอาต์พุตของgreps เหล่านั้นอาจถูกอินเตอร์ลีลอย่างไม่ถูกต้อง คุณจะต้องใช้stdbuf -oL(หากมีให้ใช้เช่นใน GNU หรือ FreeBSD) เพื่อแก้ปัญหานั้น (คุณอาจยังมีปัญหากับเส้นที่ยาวมาก (โดยทั่วไป> 4KiB)) หรือให้แต่ละเขียนเอาต์พุตในไฟล์แยกต่างหากและต่อกัน ทั้งหมดในที่สุด

นี่สตริงที่คุณกำลังมองหาอยู่ถาวร (ไม่ regexp) เพื่อให้ใช้-Fตัวเลือกที่อาจจะสร้างความแตกต่าง (ไม่น่าเป็นgrepการใช้งานทราบวิธีการเพิ่มประสิทธิภาพที่มีอยู่แล้ว)

อีกสิ่งหนึ่งที่สามารถสร้างความแตกต่างได้มากก็คือการแก้ไขโลแคลเป็น C หากคุณอยู่ในโลแคลแบบหลายไบต์:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

เพื่อหลีกเลี่ยงการมองภายใน/proc, /sys... , ใช้-xdevและระบุระบบไฟล์ที่คุณต้องการค้นหาใน:

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

หรือตัดเส้นทางที่คุณต้องการแยกอย่างชัดเจน:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

ฉันไม่คิดว่าใครบางคนสามารถชี้ให้ฉันที่ทรัพยากร - หรืออธิบาย - สิ่งที่ {} และ + หมายถึง ไม่มีอะไรที่ฉันเห็นใน man page สำหรับผู้บริหาร grep หรือค้นหาในกล่อง Solaris ที่ฉันใช้อยู่ เป็นเพียงชื่อไฟล์ที่ต่อกันกับเปลือกและส่งต่อไปยัง grep หรือไม่

3
@Poldie ที่อธิบายไว้อย่างชัดเจนในคำอธิบายของเพรดิเคต-execในหน้าโซลาริส
Stéphane Chazelas

อ่าใช่ ฉันไม่ได้หลบหนี {อักขระของฉันขณะค้นหาในหน้าเว็บมนุษย์ ลิงค์ของคุณดีกว่า ฉันพบว่าหน้าคนอ่านแย่มาก

1
RAID1 w / 3 ดิสก์? วิธีแปลก ...
tink

1
@tink, ใช่ RAID1 อยู่ในดิสก์ตั้งแต่ 2 แผ่นขึ้นไป ด้วย 3 ดิสก์เมื่อเทียบกับ 2 ดิสก์คุณเพิ่มความซ้ำซ้อนและประสิทธิภาพการอ่านขณะที่ประสิทธิภาพการเขียนจะเท่ากัน ด้วย 3 ดิสก์ซึ่งตรงข้ามกับ 2 นั่นหมายความว่าคุณสามารถแก้ไขข้อผิดพลาดได้เช่นกันเมื่อบิตเปิดหนึ่งในสำเนาคุณสามารถบอกได้ว่าสิ่งใดถูกต้องโดยตรวจสอบสำเนา 3 ชุดทั้งหมดในขณะที่มี 2 ดิสก์คุณไม่สามารถ บอกจริงๆ
Stéphane Chazelas

13

ถ้า*ในgrepการโทรไม่สำคัญกับคุณแล้วคนแรกที่ควรจะมีประสิทธิภาพมากขึ้นเป็นเพียงหนึ่งตัวอย่างของgrepการเริ่มต้นและส้อมไม่ได้ฟรี ในกรณีส่วนใหญ่มันจะเร็วกว่าแม้จะมี*แต่ในกรณีขอบการเรียงลำดับสามารถย้อนกลับได้

อาจจะมีคนอื่น ๆfind- grepโครงสร้างซึ่งทำงานได้ดีขึ้นโดยเฉพาะอย่างยิ่งกับไฟล์ขนาดเล็กจำนวนมาก การอ่านรายการไฟล์และ inodes จำนวนมากในครั้งเดียวอาจช่วยเพิ่มประสิทธิภาพการทำงานของสื่อที่หมุนได้

แต่ลองมาดูสถิติ syscall:

หา

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

grep เท่านั้น

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total

1
ในระดับของการค้นหาระบบไฟล์ทั้งหมดส้อมจะเล็กน้อย I / O คือสิ่งที่คุณต้องการลด
Gilles

แม้ว่ามันจะเป็นข้อผิดพลาดจาก OP การเปรียบเทียบไม่ถูกต้องคุณควรเอา-rธงชาติเมื่อใช้grep findคุณจะเห็นว่ามันค้นหาไฟล์เดียวกันซ้ำแล้วซ้ำอีกโดยการเปรียบเทียบจำนวนไฟล์openที่เกิดขึ้น
qwertzguy

1
@qwertzguy ไม่-rควรจะไม่เป็นอันตรายเนื่องจากการ-type fรับประกันไม่มีข้อโต้แย้งใด ๆ ที่เป็นไดเรกทอรี หลายopen()s มีแนวโน้มที่จะลงไปที่ไฟล์อื่น ๆ ที่เปิดโดยgrepในแต่ละการร้องขอ (ห้องสมุดข้อมูลการแปล ... ) (ขอบคุณสำหรับการแก้ไขคำตอบของฉัน btw)
Stéphane Chazelas

5

หากคุณอยู่บน SSD และเวลาในการค้นหานั้นน้อยมากคุณสามารถใช้ GNU ขนานได้:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

สิ่งนี้จะดำเนินการกระบวนการ grep สูงสุด 8 กระบวนการในเวลาเดียวกันตามสิ่งที่findพบ

นี่จะเป็นไดรฟ์ฮาร์ดดิสก์ แต่ SSD ก็ควรรับมือได้ดี


-1

อีกสิ่งหนึ่งที่ควรพิจารณาเกี่ยวกับสิ่งนี้คือ

จะใด ๆ ของไดเรกทอรีที่grepจะต้องซ้ำไปผ่านมีไฟล์มากกว่าระบบของคุณnofileการตั้งค่า? (เช่นจำนวนการจัดการไฟล์ที่เปิดอยู่ค่าเริ่มต้นคือ 1024 ใน distros linux ส่วนใหญ่)

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

แค่ 2 my ของฉัน


1
ทำไมจะgrepระเบิด? อย่างน้อยก็กับ GNU grep หากคุณให้เส้นทางที่มีการต่อท้าย/และใช้-Rมันเพียงแค่ทำซ้ำผ่านไดเรกทอรี เปลือกจะไม่ขยายอะไรจนกว่าคุณจะให้เปลือก globs ดังนั้นในตัวอย่างที่กำหนด ( /*) เฉพาะเนื้อหาของ/สสารไม่ใช่ของโฟลเดอร์ย่อยซึ่งจะถูกระบุโดยgrepไม่ผ่านการโต้แย้งจากเชลล์
0xC0000022L

เมื่อพิจารณาจาก OP แล้วถามเกี่ยวกับการค้นหาแบบซ้ำ ๆ (เช่น "grep -r -i 'สุนัขสีน้ำตาล' / *") ฉันได้เห็นgrepของ GNU (อย่างน้อยเวอร์ชั่น 2.9) ระเบิดด้วย: "- bash: / bin / grep: รายการอาร์กิวเมนต์ยาวเกินไป "โดยใช้การค้นหาที่แน่นอนที่ OP ใช้ในไดเรกทอรีที่มีไดเรกทอรีย่อยมากกว่า 140,000 ไดเรกทอรี
B.Kaatz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.