ค้นหาเฉพาะในไฟล์ที่ตรงกับรูปแบบที่มี ack


49

สามารถ ack ค้นหาเฉพาะไฟล์ที่ตรงกับรูปแบบ 'glob' ที่เฉพาะเจาะจงเท่านั้น (เช่น: ค้นหา foo ในไฟล์ทั้งหมดที่ชื่อ "bar * .c") คำสั่ง

ack foo "bar*.c"

ใช้งานได้เฉพาะในไดเรกทอรีปัจจุบัน

หมายเหตุ: ฉันรู้ว่ามันเป็นไปได้ด้วย find -exec:

find . -name "bar*.c" -type f -exec ack foo {} + 

แต่ฉันต้องการคำสั่ง ack ขนาดเล็กและเรียบง่ายเพราะ find ไม่ข้ามไดเรกทอรีควบคุมเวอร์ชัน


2
ผมไม่เข้าใจว่ามีอะไรผิดปกติกับการใช้find . -name "bar*.c" -exec ack foo {} \;? ไม่มีอะไรพิเศษเกี่ยวกับเรื่องgrepคุณสามารถใช้ใด ๆ-execคำสั่งด้วยหาของ
terdon

1
@terdon find ยังค้นหาจากไดเรกทอรีควบคุมเวอร์ชันและฉันไม่ต้องการมัน
compie

2
จากนั้นโปรดแก้ไขคำถามของคุณและอธิบายข้อ จำกัด ที่คุณต้องแก้ไข
terdon

คำตอบ:


28

กำลังค้นหาไดเรกทอรี

จากบทสรุปที่แสดงใน man page ฉันจะบอกว่าใช่มันสามารถประมวลผลไดเรคทอรีได้ แต่การดูสวิตช์มันไม่สามารถมองหาไฟล์ที่มีพื้นฐานมาจากแพทเทิร์น findสำหรับสิ่งที่คุณจะต้องขอความช่วยเหลือ คำสั่งackมีตัวเลือก--files-from=FILEเพื่อให้สามารถป้อนรายการไฟล์findได้

สรุป

       ack [options] PATTERN [FILE...]
       ack -f [options] [DIRECTORY...]

การใช้

   --files-from=FILE
       The list of files to be searched is specified in FILE.  The list of
       files are separated by newlines.  If FILE is "-", the list is
       loaded from standard input.

มี--ignore-file=ตัวเลือกที่อาจให้สิ่งที่คุณต้องการ แต่ดูเหมือนว่าจะใช้ความเจ็บปวดเล็กน้อย

   --ignore-file=FILTERTYPE:FILTERARGS
       Ignore files matching FILTERTYPE:FILTERARGS.  The filters are
       specified identically to file type filters as seen in "Defining
       your own types".

การค้นหาไฟล์เฉพาะประเภท

อีกวิธีเดียวที่ฉันสามารถจินตนาการถึงการทำสิ่งนี้ผ่านทางackคือการใช้--typeสวิตช์:

   --type=[no]TYPE
       Specify the types of files to include or exclude from a search.
       TYPE is a filetype, like perl or xml.  --type=perl can also be
       specified as --perl, and --type=noperl can be done as --noperl.

       If a file is of both type "foo" and "bar", specifying --foo and
       --nobar will exclude the file, because an exclusion takes
       precedence over an inclusion.

หากต้องการดูว่ามีประเภทใดให้บ้าง:

$ ack --help-types | grep -E "perl|cpp"

รูป ตัวอย่างเช่นทั้ง --type = perl และ --perl ทำงาน - [ไม่] cpp .cpp .cc .cxx .m .hpp .hh .h .h .hxx - [ไม่] objcpp .mm .h. - [ไม่] perl .pl .pm .pod .t .psgi; การจับคู่บรรทัดแรก /^#!.*\bperl/ - [ไม่] perltest .t

ตัวอย่าง

ค้นหาไฟล์ Perl ทั้งหมดตามทั้งชื่อไฟล์ (* .pm, * .pl, * .t และ * .pod) และบรรทัด shebang

$ ack -f --type perl 
examples/iwatch/iwatch/iwatch
examples/nytprof_perl/bad.pl

ค้นหาไฟล์ C ++ ทั้งหมด:

$ ack -f --type=cpp
Shared/Scanner.h
Shared/Sorter.h
Shared/DicomHelper.cpp
Shared/DicomDeviationWriter.h

ค้นหา foo ใน bar * .c

ดังนั้นคุณจะบรรลุสิ่งที่คุณต้องการได้อย่างไร คุณจะต้องใช้findวิธีนี้:

$ find adir -iname "bar*.c" | ack --files-from=- foo
adir/bar1.c
1:foo
2:foo

adir/dir1/bar1.c
1:foo
2:foo

นอกจากนี้คุณยังสามารถใช้ackความสามารถในการค้นหาไฟล์ที่ตรงกับรูปแบบที่กำหนดในชื่อไฟล์ของพวกเขา (โดยใช้-g <pattern>) จากนั้นส่งผ่านรายการนี้ไปยังการเรียกackใช้ครั้งที่สอง-xหรือ--files-from=-..

การใช้-x:

$ ack -g '\bbar.*.c$' | ack -x foo
bar1.c
1:foo
2:foo

dir1/bar1.c
1:foo
2:foo

การใช้-files-from=-:

$ ack -g '\bbar.*.c$' | ack --files-from=- foo
bar1.c
1:foo
2:foo

dir1/bar1.c
1:foo
2:foo

ไม่ว่าในกรณีใดเราจะจับคู่ชื่อไฟล์ที่คุณต้องการใช้ regex นี้:

\bbar.*.c$

ตรงนี้ไฟล์ที่มีชื่อเป็นbar.*cและสิ้นสุดหลังจากการ.cใช้จุดสิ้นสุดของเส้นยึด, $. \bนอกจากนี้เรายังมองเพื่อให้แน่ใจว่าชื่อตัวละครที่มีขอบเขตการใช้ สิ่งนี้จะล้มเหลวสำหรับไฟล์ที่มีอักขระขอบเขตเช่น$bar.cหรือ%bar.cตัวอย่าง


1
นี้ไม่ตอบคำถามของฉัน (ค้นหา foo ในไฟล์ทั้งหมดที่ชื่อ "bar * .c")
compie

1
@compie - ในแง่ที่ฉันแสดงให้คุณเห็นถึงวิธีการรับไฟล์ที่คุณต้องการ ในการค้นหาไฟล์cpp ack --type=cpp <string>ดูackหน้าคนสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ แต่สิ่งที่ฉันบอกคุณก็คือคุณไม่สามารถค้นหาโดยใช้ackวิธีที่คุณต้องการ
slm

1
ดังนั้นจึงตอบในแง่ที่แสดงackไม่สามารถทำสิ่งที่ถาม
xealits

@xealits - ส่วนนี้คำตอบมันกำลังหา foo find adir -iname "bar*.c" | ack --files-from=- fooในแถบ หรือถ้าคุณต้องการไฟล์เดียว:echo "barBaz.c" | ack --files-from=- foo
alexanderbird

2
TLDR; ack -g '\bbar.*.c$' | ack -x foo
chim

14

ง่ายถ้ารู้จักชนิดไฟล์และ ack รู้จักไฟล์หลายชนิด ตัวอย่างเช่นถ้าคุณต้องการค้นหาเฉพาะไฟล์ C เท่านั้นที่สามารถทำได้:

ack --cc 'string'

แต่ถ้าไม่ใช่ส่วนขยายที่รู้จักคุณต้องกำหนดประเภทของคุณเอง สิ่งนี้น่าจะใช้ได้:

ack --type-set barc:match:/bar.+\.c/ --barc 'string'

โปรดทราบว่าคุณต้องการทั้ง --type-set และ --barc

(ขอบคุณแอนดี้ที่ช่วยเรื่องนี้ในรายชื่อรับเมลด้วย)


1
ตกลงที่ใช้งานได้ แต่ใช้การค้นหาและ 'ack -x' นั้นง่ายกว่า ฉันคิดว่าฉันจะติดกับที่
compie

การเพิ่มลิงค์ไปยังเอกสารที่นี่จะเป็นประโยชน์ man ackไม่ได้ช่วยจริงๆและไม่มีccเมื่อฉันค้นหาสิ่งนั้น
YPCrumble

ack --help = types มีทุกอย่างที่ฉันนึกได้ ฉันส่วนใหญ่ใช้สำหรับ Python และ Ada
David Boshton

6

"มีอะไรใหม่ใน ack 2?" http://beyondgrep.com/ack-2.0/

ด้วย ack 2.0 คุณสามารถใช้ -x to filenames ใหม่จากการเรียก ack ไปยังอีกอันหนึ่ง

ack2 -g -i filepattern | ack2 -x -w searchpattern

ฉันเท่านั้นที่ไม่สามารถทำงานได้:

% ack -g "*bar*.c"
Invalid regex '*bar*.c':
Quantifier follows nothing in regex; marked by <-- HERE in m/* <-- HERE bar*.c/ at ack line 314.

ดังนั้นจึงดูเหมือนว่า -g ต้องการ regex ในขณะที่ฉันต้องการตัวเลือกสไตล์ 'glob' ...


2
ใช่-gรับ regex ไม่ใช่ glob คุณสามารถเขียน regex .*bar.*\.c$ของคุณเป็น
Andy Lester

2
@ AndyLester ขอบคุณสำหรับการยืนยัน คุณชอบไอเดียของตัวเลือก 'glob' สำหรับ ack หรือไม่
compie

3

นี่จะเป็นวิธีที่เร็วและปลอดภัยที่สุด:

find . -name '*.c' | ack -x 'some string'

-x อ่านรายการไฟล์เพื่อค้นหาจาก STDIN

อย่างไรก็ตามหากไฟล์นั้นมีแนวโน้มที่จะอยู่ในlocateฐานข้อมูลของคุณก็จะเร็วขึ้น:

locate --basename '*.c' | ack -x 'some thing'

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

locate --basename --regexp '\.\(c\|cpp\|h\|hpp\|cc\)$' |
    ack -x 'some thing'

นั่นแปลว่า: "ค้นหาชื่อไฟล์ทั้งหมดที่ลงท้ายด้วย c, cpp, h, hpp หรือ cc"


2

คำตอบไม่รองรับการเลือกไฟล์สไตล์แบบหมุน ตั้งแต่ผมพลาดจริงๆนี้ฉันสร้างเชลล์สคริปต์เล็กackg :

#!/bin/sh
# 'glob' support for ack
find -name "$2" -type f -exec ack "$1" {} +

ตอนนี้คุณสามารถใช้คำสั่ง:

ackg foo "bar*.c"

แต่โปรดทราบว่านี่จะเป็นการค้นหาในเวอร์ชันควบคุม dirs (เช่น: .git)


2

ซึ่งสามารถทำได้ด้วย-Gตัวเลือกเพื่อag ผู้ค้นหาเงิน (โคลนที่ปรับปรุงแล้วของ ack-grep)

$ echo foo > bar_wanted.c
$ echo foo > unwanted.c
$ ag -G "bar.*\.c" foo
bar_wanted.c
1:foo

หมายเหตุ:

  • agใช้ PCREไวยากรณ์นิพจน์ปกติดังนั้น regexp จะต้องแทนbar.*\.cbar*.c
  • -Gตัวเลือกความต้องการที่จะนำหน้าคำค้นหาที่เป็นอะไรหลังจากที่ถูกตีความว่าเป็นชื่อไฟล์

1

คุณต้องการรูปแบบบางอย่างหรือคุณต้องการไฟล์ต้นฉบับ C หรือไม่

หากคุณต้องการไฟล์ C ทั้งหมดให้ใช้

ack --cc foo

หากคุณต้องการให้ไฟล์ C ทั้งหมดและไฟล์ส่วนหัวไม่ใช้ C

ack --cc --no-hh foo

1

@chim คำตอบที่ดีที่สุด แต่มันเขียนเป็นความคิดเห็นดังนั้นฉัน reposting เป็นคำตอบอย่างเป็นทางการ ...

ack -g '\bbar.*.c$' | ack -x foo

ฉันชอบความจริงที่ว่าคุณสามารถ regex เส้นทาง ...


0

ใช้ zsh คุณสามารถทำได้:

ls **/a*.txt | ack --files-from=- something

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