61

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?

Braiam
  • 69,112
John Red
  • 909

6 Answers6

60

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.

wjandrea
  • 14,504
13

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" ./*

anonymous2
  • 4,325
8

Old timers would probably do this:

find . -type f -print0 | xargs -0 grep "string"
2

You can think like this, for example using grep.

grep -l PATH ~/.[^.]*

So this search for string "PATH" listing name of the files below the user's home directory, only for files that start with a dot .

/root/.bash_history
/root/.bash_profile
grep: /root/.cache: Is a directory
grep: /root/.config: Is a directory
grep: /root/.dbus: Is a directory

Using grep PATH ~/.[^.]* you'll see all occurrence, including line with searching keyword.

/root/.bash_history:echo $PATH
/root/.bash_history:echo $PATH
/root/.bash_history:PATH=$PATH:~/bin
/root/.bash_history:echo $PATH
/root/.bash_profile:PATH=$PATH:$HOME/bin
/root/.bash_profile:export PATH
grep: /root/.cache: Is a directory
grep: /root/.config: Is a directory
grep: /root/.dbus: Is a directory

To get rid of error redirect to /dev/null for example

grep PATH ~/.[^.]* 2>/dev/null

/root/.bash_history:echo $PATH
/root/.bash_history:echo $PATH
/root/.bash_history:PATH=$PATH:~/bin
/root/.bash_history:echo $PATH
/root/.bash_profile:PATH=$PATH:$HOME/bin
/root/.bash_profile:export PATH

So you can apply this pattern for searching "Apache" string in files from /etc directory-looking only in files below this main directory. You see that this don't return from /etc/httpd/conf/httpd.conf

grep Apache /etc/[^.]* 2>/dev/null
/etc/passwd:apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
/etc/services:derby-repli     4851/tcp                # Apache Derby Replication
/etc/services:derby-repli     4851/udp                # Apache Derby Replication
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.

muru
  • 207,228
Criggie
  • 719
1

Here is an example to properly skip directories without hiding all errors (without -s param on chosen answer):

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