60

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?

1
  • 1
    No, ack does not have a max-depth option, other than the -n to not recurse at all. Apr 24, 2014 at 14:41

4 Answers 4

58

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)
7
  • 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. Jan 30, 2014 at 20:15
  • 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 ... Jan 31, 2014 at 6: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. Jan 31, 2014 at 7: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, 2016 at 19:41
  • 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 2, 2020 at 13:39
34

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 *
6
  • 26
    Doesn't answer the question? (what if I want to recurse to a depth of 2)
    – Steven Lu
    Aug 25, 2015 at 17:13
  • Good point, I missed that. Any suggestions? Aug 25, 2015 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 2, 2020 at 13:48
  • Cool. FWIW I don't use ack anymore, ripgrep is where it's at.
    – Steven Lu
    Nov 2, 2020 at 20:38
  • where can I find clarification for the syntax 'grep --'? I didn't find it it 'man grep'. Sep 8, 2021 at 7:20
3

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...

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.

1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .