ทำไมคำสั่ง“ find | grep 'filename'” ช้ากว่า“ find 'filename'” มากไหม?


10

ฉันลองทั้งคำสั่งและคำสั่ง find | grep 'filename' ช้ากว่าfind 'filename' คำสั่งแบบธรรมดาหลาย ๆ ครั้ง

อะไรจะเป็นคำอธิบายที่เหมาะสมสำหรับพฤติกรรมนี้


2
คุณกำลังแสดงรายการทุกไฟล์ด้วยการค้นหาแล้วส่งข้อมูลไปยัง grep เพื่อดำเนินการ ด้วยการค้นหาที่ใช้ด้วยตัวคุณเองคุณจะพลาดขั้นตอนการผ่านทุกไฟล์ที่อยู่ในรายการเพื่อ grep เพื่อแยกเอาต์พุต สิ่งนี้จะเร็วขึ้น
Raman Sailopal

ช้าลงในแง่ใด คำสั่งใช้เวลาในการดำเนินการต่างกันหรือไม่?
Kusalananda

1
ฉันไม่สามารถทำซ้ำสิ่งนี้ในพื้นที่ ถ้ามีอะไรที่รายงานเป็นเวลานานกว่าtime find "$HOME" -name '.profile' time find "$HOME" | grep -F '.profile'(17s กับ 12s)
Kusalananda

2
@ JenniferAnderson ฉันวิ่งทั้งสองอย่างซ้ำ ๆ 17 และ 12 วินาทีเป็นค่าเฉลี่ย และใช่grepรูปแบบจะตรงกับที่ใดก็ได้ในfindผลลัพธ์ขณะที่การจับคู่กับfind -nameจะจับคู่เท่านั้น (ในกรณีนี้)
Kusalananda

2
ใช่จะเป็นไปอย่างรวดเร็วfind filename ผมเลยคิดว่านี่เป็นคำสะกดผิดและที่ OP find -name filenameหมาย ด้วยจะมีการตรวจสอบfind filenameเท่านั้นfilename(และไม่มีอะไรอื่น)
Kusalananda

คำตอบ:


11

(ฉันสมมติว่า GNU findที่นี่)

ใช้เพียง

find filename

จะรวดเร็วเพราะมันจะกลับมาfilenameหรือชื่อภายในfilenameถ้ามันเป็นไดเรกทอรีหรือข้อผิดพลาดหากชื่อนั้นไม่มีอยู่ในไดเรกทอรีปัจจุบัน มันเป็นการดำเนินการที่รวดเร็วมากคล้ายกับls filename(แต่เรียกซ้ำถ้าfilenameเป็นไดเรกทอรี)

ในทางตรงกันข้าม,

find | grep filename

จะอนุญาตให้findสร้างรายชื่อทั้งหมดจากไดเรกทอรีปัจจุบันและด้านล่างซึ่งgrepจะกรอง เห็นได้ชัดว่านี่เป็นการทำงานที่ช้ากว่ามาก

ฉันสมมติว่าสิ่งที่ตั้งใจจริงคือ

find . -type f -name 'filename'

นี่จะมองหาfilenameเป็นชื่อของไฟล์ปกติที่ใดก็ได้ในไดเรกทอรีปัจจุบันหรือด้านล่าง

นี้จะเป็นอย่างรวดเร็ว (หรือปานด่วน) เป็นfind | grep filenameแต่grepการแก้ปัญหาจะตรงfilenameกับเส้นทางแบบเต็มของแต่ละชื่อที่พบคล้ายกับสิ่งที่จะทำอย่างไรกับ-path '*filename*'find


ความสับสนนั้นมาจากการเข้าใจผิดเกี่ยวกับวิธีการfindทำงาน

ยูทิลิตีใช้จำนวนเส้นทางและส่งคืนชื่อทั้งหมดที่อยู่ใต้เส้นทางเหล่านี้

จากนั้นคุณสามารถจำกัดชื่อที่ส่งคืนโดยใช้การทดสอบต่างๆที่อาจทำงานกับชื่อไฟล์พา ธ การประทับเวลาขนาดไฟล์ประเภทของไฟล์และอื่น ๆ

เมื่อคุณพูด

find a b c

คุณถามfindไปยังรายชื่อทุกคนอยู่ภายใต้สามเส้นทางa, และb cหากสิ่งเหล่านี้เกิดขึ้นเป็นชื่อของไฟล์ปกติในไดเรกทอรีปัจจุบันสิ่งเหล่านี้จะถูกส่งคืน หากใด ๆ ของพวกเขาเกิดขึ้นเป็นชื่อของไดเรกทอรีแล้วมันจะถูกส่งกลับพร้อมกับชื่อเพิ่มเติมทั้งหมดภายในไดเรกทอรีนั้น

เมื่อฉันทำ

find . -type f -name 'filename'

สิ่งนี้จะสร้างรายชื่อทั้งหมดในไดเรกทอรีปัจจุบัน ( .) และด้านล่าง จากนั้นก็จะ จำกัด ชื่อให้กับผู้ที่ไฟล์ปกติเช่นไม่ไดเรกทอรี ฯลฯ -type fมี จากนั้นก็มีข้อ จำกัด ต่อไปชื่อที่ตรงกับการใช้filename -name 'filename'สตริงfilenameนั้นอาจเป็นรูปแบบชื่อไฟล์ที่กำลังวนรอบเช่น*.txt(โปรดจำไว้ว่าให้พูด!)

ตัวอย่าง:

ดูเหมือนว่าต่อไปนี้จะ "ค้นหา" ไฟล์ที่เรียกว่า.profileในไดเรกทอรีบ้านของฉัน:

$ pwd
/home/kk
$ find .profile
.profile

แต่ในความเป็นจริงมันแค่คืนค่าชื่อทั้งหมดที่พา ธ.profile(มีเพียงหนึ่งชื่อเท่านั้นและนั่นคือไฟล์นี้)

จากนั้นฉันcdขึ้นหนึ่งระดับแล้วลองอีกครั้ง:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

คำสั่งในขณะนี้ไม่พบเส้นทางที่เรียกว่าfind.profile

อย่างไรก็ตามถ้าฉันให้มันไปดูที่ไดเรกทอรีปัจจุบันและจากนั้นจำกัด ชื่อที่ส่งคืนไปเท่านั้น.profileมันก็พบว่าจากที่นั่นเช่นกัน:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenameจะกลับมาเฉพาะในfilenameกรณีที่filenameไม่ได้เป็นของไดเรกทอรีประเภท(หรือเป็นของไดเรกทอรีประเภท แต่ไม่มีรายการใด ๆ ตัวเอง)
Stéphane Chazelas

2

คำอธิบายที่ไม่ใช่ด้านเทคนิค: การมองหาแจ็คในฝูงชนนั้นเร็วกว่าการมองหาทุกคนในฝูงชนและกำจัดทั้งหมดจากการพิจารณายกเว้นแจ็ค


ปัญหาคือว่า OP คาดว่าแจ็คจะเป็นคนเดียวในฝูงชน ถ้าเป็นพวกเขาโชคดี find jackจะแสดงรายการjackหากเป็นไฟล์ชื่อjackหรือชื่อทั้งหมดในไดเรกทอรีหากเป็นไดเรกทอรี มันเป็นความเข้าใจผิดของวิธีการfindทำงาน
Kusalananda

1

ฉันยังไม่เข้าใจปัญหา แต่สามารถให้ข้อมูลเชิงลึกเพิ่มเติม

เช่นเดียวกับ Kusalananda การfind | grepโทรนั้นเร็วกว่าชัดเจนในระบบของฉันซึ่งไม่สมเหตุสมผล ตอนแรกฉันคิดว่าปัญหาบัฟเฟอร์บางอย่าง; การเขียนไปยังคอนโซลจะทำให้เวลาช้าลงไปยัง syscall ถัดไปเพื่ออ่านชื่อไฟล์ถัดไป การเขียนไปที่ไพพ์นั้นเร็วมาก: ประมาณ 40MiB / s แม้กระทั่งการเขียนแบบ 32 ไบต์ (ในระบบที่ค่อนข้างช้าของฉัน 300 MiB / s สำหรับขนาดบล็อก 1MiB) ดังนั้นฉันจึงสันนิษฐานว่าfindสามารถอ่านจากระบบไฟล์ได้เร็วขึ้นเมื่อเขียนไปที่ไพพ์ (หรือไฟล์) เพื่อให้ทั้งสองการดำเนินการอ่านพา ธ ไฟล์และการเขียนไปยังคอนโซลสามารถทำงานแบบขนาน (ซึ่งfindกระบวนการเธรดเดี่ยวไม่สามารถทำได้ด้วยตนเอง

มันเป็นfindความผิด

เปรียบเทียบทั้งสองสาย

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

และ

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

แสดงให้เห็นว่าfindทำสิ่งที่โง่อย่างไม่น่าเชื่อ (สิ่งที่อาจเป็น) -name '*.txt'มันก็จะเปิดออกจะค่อนข้างไร้ความสามารถในการดำเนินการ

อาจขึ้นอยู่กับอัตราส่วนอินพุต / เอาต์พุต

คุณอาจคิดว่าfind -nameชนะถ้ามีน้อยมากที่จะเขียน แต่มันน่าอายfindกว่า มันจะสูญเสียแม้ว่าจะไม่มีอะไรจะเขียนเลยกับไฟล์ 200K (13M ของ data pipe) สำหรับgrep:

time find /usr -name lwevhewoivhol

findสามารถเป็นอย่างรวดเร็วgrepแม้ว่า

ปรากฎว่าความfindโง่เขลาของnameไม่ได้ครอบคลุมถึงการทดสอบอื่น ๆ ใช้ regex แทนและปัญหาจะหายไป:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

ฉันคิดว่านี่ถือได้ว่าเป็นข้อผิดพลาด ทุกคนยินดีที่จะรายงานข้อผิดพลาด? เวอร์ชันของฉันคือ find (GNU findutils) 4.6.0


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

@psmears แน่นอนฉันได้ทำการทดสอบหลายครั้งแล้ว ปัญหาการแคชได้รับการกล่าวถึงแม้ในความคิดเห็นของคำถามก่อนคำตอบแรก findรุ่นของฉันคือ find (GNU findutils) 4.6.0
Hauke ​​Laging

ทำไมมันจึงน่าแปลกใจที่การเพิ่ม-name '*.txt'ช้าลงfind? ต้องทำงานพิเศษทดสอบชื่อไฟล์แต่ละไฟล์
Barmar

@Barar หนึ่งในมือนี้งานพิเศษที่สามารถทำได้อย่างรวดเร็วมาก ในทางกลับกันงานพิเศษนี้จะบันทึกงานอื่น findต้องเขียนข้อมูลน้อยลง และการเขียนไปยังไปป์เป็นการทำงานที่ช้ากว่ามาก
Hauke ​​Laging

การเขียนไปยังดิสก์นั้นช้ามากการเขียนลงในไพพ์นั้นไม่เลวเลยเพียงแค่คัดลอกไปยังเคอร์เนลบัฟเฟอร์ โปรดสังเกตว่าในการทดสอบครั้งแรกของคุณให้เขียนมากขึ้นเพื่อ/dev/nullใช้เวลาระบบน้อยลง
Barmar

0

หมายเหตุ : ฉันจะสมมติว่าคุณหมายถึงfind . -name filename(มิฉะนั้นคุณกำลังมองหาสิ่งต่าง ๆfind filenameจริง ๆ แล้วมองเข้าไปในเส้นทางที่เรียกว่าชื่อไฟล์ซึ่งอาจมีไฟล์เกือบจะไม่มีไฟล์


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

ดังนั้นเมื่อคุณถามfindเพื่อค้นหาไฟล์ที่มีชื่อเพียง แต่ต้องใช้การตรวจสอบfindจะถามสำหรับที่ไฟล์และแฟ้มที่เพียงเพื่อระบบแฟ้มพื้นฐานซึ่งจะอ่านหน้าน้อยมากจากการจัดเก็บมวล ดังนั้นหากระบบไฟล์มีค่าเกลือการดำเนินการนี้จะทำงานได้เร็วกว่าการสำรวจทรีทั้งหมดเพื่อดึงรายการทั้งหมด

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


-2

ให้สมมติว่าไฟล์ / john / paul / george / ringo / beatles มีอยู่และไฟล์ที่คุณกำลังค้นหาเรียกว่า 'stones'

find / stones

find จะเปรียบเทียบ 'beatles' กับ 'stones' และวางลงเมื่อ 's' และ 'b' ไม่ตรงกัน

find / | grep stones

ในกรณีนี้การค้นหาจะผ่าน '/ john / paul / george / ringo / beatles' ไปยัง grep และ grep จะต้องทำงานผ่านเส้นทางทั้งหมดก่อนที่จะพิจารณาว่าการแข่งขันของมัน

grep ทำงานหนักมากขึ้นด้วยเหตุนี้จึงใช้เวลานาน


1
คุณลองดูบ้างไหม?
Hauke ​​Laging

3
ค่าใช้จ่ายของการเปรียบเทียบสตริง (ง่ายมากและราคาถูก) ถูกแคระโดยสมบูรณ์ IO (หรือเพียงแค่ syscall ถ้าแคช) ค่าใช้จ่ายของการค้นหาไดเรกทอรี
Mat

grep ไม่ใช่การเปรียบเทียบสตริงการเปรียบเทียบนิพจน์ปกติซึ่งหมายความว่าจะต้องดำเนินการผ่านสตริงทั้งหมดจนกว่าจะพบการแข่งขันหรือถึงจุดสิ้นสุด การค้นหาไดเรกทอรีเหมือนกันไม่ว่าจะเกิดอะไรขึ้น
Paranoid

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