วิธีค้นหาไฟล์ที่มีสองคำที่แตกต่างกันอยู่?


14

ฉันกำลังมองหาวิธีการค้นหาไฟล์ที่มีอินสแตนซ์ของคำสองคำอยู่ในไฟล์เดียวกัน ฉันใช้สิ่งต่อไปนี้เพื่อทำการค้นหาจนถึงจุดนี้:

find . -exec grep -l "FIND ME" {} \;

ปัญหาที่ฉันพบคือหากไม่มีช่องว่างตรงระหว่าง "FIND" และ "ME" ผลการค้นหาจะไม่ให้ไฟล์ ฉันจะปรับสตริงการค้นหาเดิมที่ทั้งคำว่า "FIND" และ "ME มีอยู่ในไฟล์ได้อย่างไรเมื่อเทียบกับ" FIND ME "

ฉันใช้ AIX


1
มีคำอยู่ที่ใดก็ได้ในไฟล์หรืออยู่ในบรรทัดเดียวกันเสมอ?
Sobrique

ความตั้งใจเป็นเส้นเดียวกัน
แช้ดแฮร์ริสัน

ทางเลือกถ้าคำที่อยู่ในบรรทัดเดียวกันคือการใช้การแสดงออกปกติด้วยgrep -E/ egrepที่อธิบายถึงรูปแบบทั้งหมดที่คุณมีความสนใจใน (และใช้+แทน;ถ้าคุณพบมีการสนับสนุน+.
MattBianco

คำตอบ:


21

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

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

คุณสามารถทำมาตรฐาน:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

แต่นั่นจะรันสอง greps ต่อไฟล์ หากต้องการหลีกเลี่ยงการเรียกใช้ไฟล์จำนวนมากgrepและยังพกพาได้ในขณะที่ยังอนุญาตให้ใช้อักขระใด ๆ ในชื่อไฟล์คุณสามารถทำได้:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

แนวคิดที่จะแปลงผลลัพธ์ของfindรูปแบบที่เหมาะสมสำหรับ xargs (ที่คาดว่าจะว่างเปล่า (SPC / TAB / NL และช่องว่างอื่น ๆ จากสถานที่ของคุณด้วยการใช้งานบางส่วนของxargs) รายการแยกของคำที่เดียวคำพูดสองครั้งและเครื่องหมายแบ็กสแลช ช่องว่างหลบหนีและกันและกัน)

โดยทั่วไปแล้วคุณไม่สามารถประมวลผลเอาต์พุตได้find -printเนื่องจากจะแยกชื่อไฟล์ด้วยอักขระขึ้นบรรทัดใหม่และไม่หนีอักขระขึ้นบรรทัดใหม่ที่พบในชื่อไฟล์ เช่นถ้าเราเห็น:

./a
./b

เรามีวิธีที่จะรู้ว่าไม่ว่าจะเป็นไฟล์เดียวเรียกว่าไม่มีbในไดเรกทอรีที่เรียกว่าa<NL>.หรือถ้ามันเป็นสองไฟล์และab

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

หากเรานำตัวอย่างข้างต้นfindจะแสดงผลในกรณีแรก (หนึ่งไฟล์):

.//a
./b

awk ใดที่หนีไป:

.//a\
./b

เพื่อให้xargsเห็นว่ามันเป็นข้อโต้แย้งอย่างหนึ่ง และในกรณีที่สอง (สองไฟล์):

.//a
.//b

ซึ่งawkจะปล่อยให้เป็นไปตามดังนั้นxargsเห็นสองข้อโต้แย้ง


ทำไมไม่ใช้find ... -print0และgrep --nullแทน?
razzed

@razzed ไม่แน่ใจว่าคุณหมายถึงอะไร grep --null(aka -Z) ใช้ในอันแรก แต่เป็นส่วนขยายของ GNU -print0(ส่วนขยาย GNU อื่น) จะไม่ช่วยที่นี่
Stéphane Chazelas

ขอบคุณ ฉันต้องการตัดเชลล์โค้ดของคุณลงในสคริปต์ซึ่งนำไดเรกทอรีการค้นหาเป็นอาร์กิวเมนต์จากบรรทัดคำสั่ง ฉันยังไม่แน่ใจว่ามีความ.//.หมายอะไรและสงสัยว่าฉันจะแก้ไขเพื่อยอมรับการโต้แย้งจากบรรทัดคำสั่งได้$1อย่างไร
ทิม

ขอบคุณ ในคำสั่งของคุณจำเป็นต้องใช้-print0กับfindและ-0กับxargs?
ทิม

@Tim ไม่แน่ใจว่าคุณหมายถึงอะไร ฉันไม่ได้ใช้find -print0ทุกคำตอบ
Stéphane Chazelas

8

ถ้าไฟล์ที่อยู่ในไดเรกทอรีเดียวและชื่อของพวกเขาไม่ได้มีพื้นที่แท็บขึ้นบรรทัดใหม่*, ?หรือ[ตัวอักษรและไม่ได้เริ่มต้นด้วย-มิได้.นี้จะได้รับรายชื่อของไฟล์ที่มี ME แล้วแคบลงเพื่อให้คนที่ว่า ยังมีการค้นหา

grep -l FIND `grep -l ME *`

ต้องใช้ upvotes มากกว่านี้ !! หรูหรากว่าคำตอบ "ยอมรับ" ทำงานให้ฉัน
roblogic

เพิ่งทำgrep -l CategoryLinearAxis `grep -l labelJsFunction *`ในขณะที่ค้นหาไฟล์ที่มีทั้งสองอย่าง ช่างเป็นวิธีที่สมบูรณ์แบบที่สุด +1
WEBjuju

3

ด้วยawkคุณสามารถเรียกใช้:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

มันใช้cxและcyการนับสำหรับสายการจับคู่และตามลำดับFIND MEในENDบล็อกถ้าทั้งสองเคาน์เตอร์> 0 FILENAMEมันพิมพ์
สิ่งนี้จะเร็วขึ้น / มีประสิทธิภาพยิ่งขึ้นด้วยgnu awk:

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +

2

หรือใช้egrep -eหรือgrep -Eชอบสิ่งนี้:

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

หรือ

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

การ+ค้นหาทำให้ (ถ้าสนับสนุน) เพิ่มชื่อไฟล์ (พา ธ ) หลายไฟล์เป็นอาร์กิวเมนต์ในคำสั่งที่กำลัง-execแก้ไข สิ่งนี้บันทึกกระบวนการและเร็วกว่ามาก\;ที่จะเรียกใช้คำสั่งหนึ่งครั้งสำหรับแต่ละไฟล์ที่พบ

-type f จับคู่ไฟล์เท่านั้นเพื่อหลีกเลี่ยงการ grepping ในไดเรกทอรี

'(ME.*FIND|FIND.*ME)'เป็นนิพจน์ทั่วไปที่ตรงกับบรรทัดใด ๆ ที่มี "ME" ตามด้วย "FIND" หรือ "FIND" ตามด้วย "ME" (เครื่องหมายคำพูดเดี่ยวเพื่อป้องกันเชลล์จากการตีความอักขระพิเศษ)

เพิ่ม-iไปยังgrepคำสั่งที่จะทำให้มันกรณีตาย

เพียงตรงกับสายที่ "Find" มาก่อน "ME" 'FIND.*ME'การใช้งาน

หากต้องการช่องว่าง (1 หรือมากกว่านั้น แต่ไม่มีอะไรอื่น) ระหว่างคำ: 'FIND +ME'

หากต้องการอนุญาตให้มีช่องว่าง (0 หรือมากกว่า แต่ไม่มีอะไรอื่น) ระหว่างคำ: 'FIND *ME'

ชุดค่าผสมนั้นไม่มีที่สิ้นสุดด้วยนิพจน์ทั่วไปและให้คุณสนใจในการจับคู่เฉพาะแบบแถวต่อครั้งเช่น egrep มีประสิทธิภาพมาก


greps ส่วนใหญ่ไม่รองรับ "-r" หรือไม่? ที่จะกำจัด "ค้นหา" แต่อาจมีซ็อกเก็ตหรือไฟล์ที่ไม่ใช่ไฟล์ธรรมดาในแผนผังที่กำลังค้นหา
ขโมยช่วงเวลาตั้งแต่

OP ใช้ AIX และมีfindคำถาม
MattBianco

0

เมื่อดูคำตอบที่ยอมรับดูเหมือนว่าจะซับซ้อนกว่าที่คิด รุ่น GNU findและgrepและxargsสนับสนุนสตริงที่สิ้นสุดด้วย NULL มันง่ายเหมือน:

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

คุณสามารถแก้ไขfindคำสั่งของคุณเพื่อกรองไฟล์ที่คุณต้องการและทำงานกับชื่อไฟล์ที่มีอักขระใด ๆ โดยไม่ต้องเพิ่มความซับซ้อนในการsedแยกวิเคราะห์ หากคุณต้องการประมวลผลไฟล์เพิ่มเติมให้เพิ่มไฟล์--nullสุดท้ายgrep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

และเป็นฟังก์ชั่น:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

เห็นได้ชัดว่าใช้คำตอบที่ยอมรับถ้าคุณไม่ได้ใช้เครื่องมือเหล่านี้ในเวอร์ชัน GNU


1
--null, --print0, -0มีทั้งหมดส่วนขยาย GNU แม้ว่าบางส่วนของพวกเขาจะพบในการใช้งานอื่น ๆ ในปัจจุบันพวกเขายังคงไม่ได้พกพาและไม่ได้อยู่ในมาตรฐาน POSIX หรือ Unix
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.