30

I want to search for a string of text in all files in a directory (and not its subdirectories; I know the -r option does that, but that is not what I want).

  1. Running

    grep "string" /path/to/dir
    

    is supposed to be able to do this, I've read, but it gives me the error:

    grep: dir: Is a directory

  2. Next, I tried running grep on multiple files.

    grep "string" .bashrc .bash_aliases works perfectly.

    grep "string" .bash* works as intended too.

    grep "string" * gives me the errors:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

Only the errors are printed, I don't get the matching lines. I tried using the -s option, but to no avail.

So, my questions:

  1. Why am I not being able to use grep on a directory, as in (1), when I should be able to? I've seen that done in plenty examples on the Internet.
    Edit: When I say "using grep on a directory", I mean "search in all the files in that directory excluding its subdirectories". I believe that this is what grep does when you pass a directory to it in place of a file. Am I incorrect?

  2. Please give me an explanation on the workings of grep that would explain the behavior of commands in (2).
    Edit: Let me be more specific. Why does using wildcards to specify multiple files to search in for work with .bash* and not with * or even ./*?

  3. How can I search all the files in a directory (and not its subdirectories) using grep?

  • Also you're relying on the shell expanding wildcards such as *, known as globbing. Globbing does not include filenames starting with a dot such as .bashrc as standard. You can set shell options so that it will include these files, but you can get yourself in a bit of a mess if you don't know what you're doing. A good guide to understanding globing can be found here mywiki.wooledge.org/glob – Arronical May 25 '16 at 16:23
  • I dunno why, but I've always done globbing on hidden files, and it has always worked. I haven't change any setting or something. As I pointed out in (2), it works with grep "string" .bash* too. – John Red May 25 '16 at 16:32
  • Sorry, my last example was incorrect. You can search in hidden files as well, and suppressing the "is a directory" because Linux technically sees directories as a different type of file. The command would be then: grep "string" * .* 2>/dev/null or grep -s "string" * .* – Terrance May 25 '16 at 16:55
  • Also this stackoverflow.com/q/9217185/3701431 – Sergiy Kolodyazhnyy Jun 7 '18 at 20:49
35

In Bash, a glob will not expand into hidden files, so if you want to search all the files in a directory, you need to specify hidden files .* and non-hidden *.

To avoid the "Is a directory" errors, you could use -d skip, but on my system I also get an error grep: .gvfs: Permission denied, so I suggest using -s, which hides all error messages.

So the command you are looking for is:

grep -s "string" * .*

If you are searching files in another dir:

grep -s "string" /path/to/dir/{*,.*}

Another option is to use the dotglob shell option, which will make a glob include hidden files.

shopt -s dotglob
grep -s "string" *

For files in another dir:

grep -s "string" /path/to/dir/*

† Someone mentioned that I shouldn't get this error. They may be right - I did some reading but couldn't make heads or tails of it myself.

  • Is there any reason for the space between * and .*? – Hashim Sep 23 '18 at 0:35
  • 2
    @Hashim Compare the output of echo * .* and echo *.* run in your home directory, and the difference should be obvious. Otherwise LMK and I'll explain it. – wjandrea Sep 23 '18 at 1:03
  • Interesting, so echo * shows non-hidden files and folders, echo *.* shows non-hidden files, echo .* shows all files, and echo * .* shows all files and directories. But why the reason for the space between the two in the latter case? It feels messy to me. Is there not a way to combine the two to get the same results? Or otherwise is there a syntax explanation of why the two need to be separated here, or is * .* an exceptional case? – Hashim Sep 23 '18 at 2:00
  • 1
    @Hashim I'm not sure how you came to those conclusions, so let me explain. First, directories are files in this context. In globs, * represents all non-hidden files (i.e. filenames which don't start with a dot); .* represents all hidden files (i.e. filenames that do start with a dot); and *.* represents all non-hidden files which contain a dot. In echo * .*, the two globs must be separate because they are different globs: one for non-hidden, one for hidden. Though as I wrote in my answer, you can make * include hidden files by turning on the dotglob shell option. – wjandrea Sep 23 '18 at 2:23
  • 1
    Using *.* is common on Windows (DOS) as a way to list all files but on *nix will only include files with a dot in them, so it doesn't make sense on *nix. Instead you use * to list all files except hidden files, and .* to list hidden files. – thomasrutter Mar 6 at 1:05
10

You need the -d skip option added on.

  1. Grep is searching inside of files. You can search recursively, as you said, if you want to search files inside of a directory.

  2. By default, grep will read all files, and it detects the directories. Because by default you have not defined what to do with the directories with the -d option, it give error output.

  3. Searching just within the parent directory would be `grep -d skip "string" ./*

  • For more information on grep, see man grep. – anonymous2 May 25 '16 at 16:14
  • (a) Please see the edit. (b) Using -d skip does not work; it's basically the same as -s; also, see the edit. (c) Nope, grep -d skip "string" ./* does not work either. – John Red May 25 '16 at 16:40
7

Old timers would probably do this:

find . -type f -print0 | xargs -0 grep "string"
  • 3
    Why not find . -type f -exec grep string {} +? – wchargin May 26 '16 at 1:07
  • 5
    You also want -maxdepth 1. – wchargin May 26 '16 at 5:48
  • @wchargin: if you want the file name in the output when there's only one file I think you want find . -type f -maxdepth 1 -exec grep string /dev/null {} + – Gregory Nisbet Mar 14 at 6:27
  • 1
    @GregoryNisbet: Just pass -H to grep. – wchargin Mar 14 at 17:23
2

Rephrasing - you want to grep the files in one level of subdirectory, but not recurse though all sub-sub directories?

grep forthis  *  */*

Or if you don't want the files in the current directory

grep forthis  */*

Note this won't find directories starting with a dot.

grep forthis  .*/*    */*   

should do that job.

There's also -maxdepth and -mindepth restriction parameters available to the find command too.

  • Wouldn't grep forthis */* search files both in the current directory and one directory down? – Hashim Sep 23 '18 at 0:37
  • @Hashim nope mostly - cos */* only matches things with one slash. If you had a file named a/b in the current directory then `*/* would match that. – Criggie Sep 23 '18 at 4:07
0

Here is an example to skip directories without skipping all errors:

grep --directories='skip' 'searchString' *

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.