58

Is there any way to tell ack to only search for text on the current folder? (or specify a max-depth level?) And with grep?

Braiam
  • 35,380
  • 25
  • 108
  • 167
Amelio Vazquez-Reina
  • 40,169
  • 77
  • 197
  • 294

4 Answers4

56

You can couple find with the -exec argument. Example:

find . -maxdepth 1 -exec grep foo {} \;

This can be scaled, i.e. -maxdepth 2.

Edit

As mentioned in the [answer by @Stéphane Chazelas], it is advisable to restrict find to regular files so that grep doesn't produce an error when the argument {} actually is a directory path:

find . -maxdepth 1 -type f -exec grep -H foo {} \;
  • -type f is a filter for find that limits the search results to files
  • -H is a grep option used to print a filename for every match (desired behavior when more than one file match)
AdminBee
  • 21,637
  • 21
  • 47
  • 71
David Wilkins
  • 1,030
  • 7
  • 16
  • 1
    Stephane, my answer (`-maxdepth 1`) was scaleable. For example `-maxdepth 2`. I do not know how to describe the edit you made as such. – David Wilkins Jan 30 '14 at 20:15
  • it will not reach @stephane if you don't use the @. – Braiam Jan 31 '14 at 05:26
  • 6
    Yes sorry, I agree my edit was too much intrusive. You may still want to clarify that that (`-maxdepth`) is not portable/standard syntax (only GNU and some BSDs). Also, there's no point using `\;` here (run one `grep` per file). Use `grep -H foo {} +` (GNU specific) or `grep foo /dev/null {} +` (standard) to make sure the file name is always printed. The standard equivalent to `-maxdepth 2` would be `find . -path './*/*' -type d -prune -o -type f -exec ...` – Stéphane Chazelas Jan 31 '14 at 06:57
  • 4
    Also note that yours will give error messages for directories (including `.` as you don't give the `-mindepth 1`) while GNU `grep` will not try to read directories with `-r` (it _recurses_ on them). You may want to add a `-d skip` to `grep` (assuming GNU grep) or better add `! -type d` to `find` or even better `-type f` (or `-xtype f` assuming GNU `find`) as you probably don't want `grep` to read non-regular files. – Stéphane Chazelas Jan 31 '14 at 07:01
  • 6
    I tend to use `grep -Hin` with this approach so that I can see the filename and the line that the occurrence is on. – GDP2 Apr 11 '16 at 19:41
  • VAT DA FAAK?!? Make grep a --max-depth parameter! – uav Dec 19 '19 at 21:28
  • 1
    @uav feel free to send a patch to the GNU utils mailing list. The idea behind GNU tools is that each tool should have a limited, specialized set of features and be used in cooperation. GNU `find` has all the features you need to drill down a file system, filtering out anything you don't want. Embedding it within grep, sed and gawk seems like a lot of redundant effort. – sleblanc Nov 02 '20 at 13:39
33

Use -n for no-recurse:

$ ack -n foo

grep is not recursive by default, and you should use the -r flag only if you want a recursive search.

You can search the current directory with grep as follows:

$ grep -- foo *
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Eric Wilson
  • 4,622
  • 9
  • 32
  • 43
  • 26
    Doesn't answer the question? (what if I want to recurse to a depth of 2) – Steven Lu Aug 25 '15 at 17:13
  • Good point, I missed that. Any suggestions? – Eric Wilson Aug 25 '15 at 21:33
  • @StevenLu, you can recurse a number of directories by using globs: `ack -n pattern */*/`. It has several caveats, like expanding directories with an extreme number of subdirectories. If you make sure that the glob has a slash at the end, then it will only expand directory names, so that should limit the potential for damage, a bit. – sleblanc Nov 02 '20 at 13:48
  • Cool. FWIW I don't use ack anymore, ripgrep is where it's at. – Steven Lu Nov 02 '20 at 20:38
  • where can I find clarification for the syntax 'grep --'? I didn't find it it 'man grep'. – user3804598 Sep 08 '21 at 07:20
  • @user3804598 it's not a `grep` thing, it's a shell thing. From `man bash`: "A -- signals the end of options and disables further option processing. Any arguments after the -- are treated as filenames and arguments." – J.M. Janzen Aug 22 '23 at 20:55
2

For an equivalent of GNU grep -r foo . that looks only in regular files in the current directory and not any of the subdirectories, you can do:

  • zsh and GNU grep or compatible:

    grep -H foo ./*(.D)
    
  • standard find and grep from any shell:

    find . ! -name . -prune -type f -exec grep foo /dev/null {} +
    
  • GNU find and GNU grep (or compatible) from any shell:

    find . -maxdepth 1 -type f -exec grep -H foo {} +
    

To look for files at depths 1 to 3 (in ./file, ./subdir/file, ./subdir/subsubdir/file, but not any deeper):

  • GNU find and GNU grep (or compatible) from any shell:

    find . -maxdepth 3 -type f -exec grep -H foo {} +
    
  • standard find and grep, but assuming file names are valid text in the locale:

    find . '(' ! -path './*/*/*' -o -prune ')' -o -exec grep foo /dev/null {} +
    
  • zsh and GNU grep or compatible:

    set -o extendedglob # best in ~/.zshrc
    grep -H foo ./**/*~./*/*/*/*(D.)
    

    though note that it's inefficient as it still looks for files at any depth before excluding them with the ~ extendedglob operator.

-H (GNU extension) / /dev/null is to make sure grep prints the name of the file even if only one file is given to it.

-type f or zsh's . glob qualifier is to select only regular files (like GNU grep does by default with -r nowadays), to the exclusion of all other types of files such as symlinks (even if they point to regular files), directories, fifos, devices...

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
1

I'm obviously late to the topic, but this may prove useful to a future reader. After reading the comments and answers above, perhaps a wrapper script for grep is in order until GNU find gains a -maxdepth option. Using a pattern like the following,

 grep -Hin foo `find . -maxdepth 1 -type f`

the following script will effectively wrap the pair of commands:

#! /bin/sh
#
# USAGE: grep-maxdepth <NUM> <GREP_ARGS>
#
#    <NUM>        integer specifying the maxdepth passed to find
#    <GREP_ARGS>  arguments provided to grep
#
depth=$1
shift
# DON'T DO THIS ! grep $* `find . -maxdepth ${depth} -type f`
# DO this based upon comments
find . -maxdepth ${depth} -type f -exec grep $* {} \;

This solution should work regardless of the availability of GNU grep; however, the usage of -maxdepth for find implies the installation of GNU's Tools.

I don't use ack, but this simple solution should work for it as well.

NOTE: Trivially-edited based upon comment from Stéphane Chazelas. This should now properly-accommodate white space and other characters in file names. Thanks.

  • That's wrong for a number of reasons. See [Why is looping over find's output bad practice?](//unix.stackexchange.com/q/321697) and [Security implications of forgetting to quote a variable in bash/POSIX shells](//unix.stackexchange.com/q/171346) for instance. – Stéphane Chazelas Mar 10 '21 at 17:37