176

Starting from (notice the wildcards before and after "some text")

find . -type f -name '*some text*'

how can one exclude/ignore all hidden files and directories?

I've already been googling for far too long, came across some -prune and ! (exclamation mark) parameters, but no fitting (and parsimonious) example which just worked.

Piping | to grep would be an option and I'd also welcome examples of that; but primarily I'm interested in a brief one-liner (or a couple of stand-alone one-liners, illustrating different ways of achieving the same command-line goal) just using find.

ps: Find files in linux and exclude specific directories seems closely related, but a) is not accepted yet and b) is related-but-different-and-distinct, but c) may provide inspiration and help pinpoint the confusion!

Edit

find . \( ! -regex '.*/\..*' \) -type f -name "whatever", works. The regex looks for "anything, then a slash, then a dot, then anything" (i.e. all hidden files and folders including their subfolders), and the "!" negates the regex.

2

11 Answers 11

198

This prints all files that are descendants of your directory, skipping hidden files and directories:

find . -not -path '*/.*'

So if you're looking for a file with some text in its name, and you want to skip hidden files and directories, run:

find . -not -path '*/.*' -type f -name '*some text*'

Explanation:

The -path option runs checks a pattern against the entire path string. * is a wildcard, / is a directory separator, . is a dot (hidden filenames start with a dot on Linux), and * is another wildcard. -not means don't select files that match this test.

I don't think that find is smart enough to avoid recursively searching hidden directories in the previous command, so if you need speed, use -prune instead, like this:

 find . -type d -path '*/.*' -prune -o -not -name '.*' -type f -name '*some text*' -print
6
  • Note with the last one that you need that -print at the end! Also, not sure if -name '\.*' would be more efficient instead of -path` (because the path is searching subpaths, but these will be pruned out) Apr 29, 2014 at 11:18
  • 1
    What's the special meaning of . in this context? Jan 15, 2015 at 15:40
  • 3
    @frostschutz Actually, come to think of it, I may be wrong about . having special meaning.
    – Flimm
    Aug 5, 2016 at 13:00
  • 2
    @Flimm yup, no need to escape .. As far as I'm aware, only these need to be escaped: *, ?, and [].
    – thdoan
    Feb 15, 2017 at 9:16
  • This is good, but could be substantially simplified. See my answer
    – De Novo
    Mar 19, 2019 at 20:33
15

This is one of the few means of excludes dot-files that also works correctly on BSD, Mac and Linux:

find "$PWD" -name ".*" -prune -o -print
  • $PWD print the full path to the current directory so that the path does not start with ./
  • -name ".*" -prune matches any files or directories that start with a dot and then don't descend
  • -o -print means print the file name if the previous expression did not match anything. Using -print or -print0 causes all other expressions to not print by default.
6
  • Please explain / elaborate on "alarmingly complicated"; the answers already given and your answer seem to give evidence to the contrary...? Mar 24, 2016 at 22:13
  • 2
    "alarmingly complicated" is probably excessive. I reworded the answer to get to the point. I think the answer I posted is difficult to understand and is hard to see without a very careful reading of the man page. If you are only using GNU find then there are more possible solutions.
    – eradman
    Mar 28, 2016 at 14:50
  • -o ... -print is helpful. For my use, I now have find ... '!' -name . -name '.*' -prune -o ... -print, which was more convenient than including $PWD.
    – Roger Pate
    Aug 24, 2016 at 19:28
  • tried using it in combination with a dir name of "." - did not result in anything. May 26, 2021 at 8:28
  • this seems like the ticket to me. fwiw I'd consider leaving off the "$PWD" thing. While it may be what you prefer, I think it's orthogonal to the question. Sep 21, 2021 at 17:42
6

You don't have to use find for that. Just use globstar in shell it-self, like:

echo **/*foo*

or:

ls **/*foo*

where **/ represents any folder recursively and *foo* any file which has foo in its name.

By default using ls will print file names excluding hidden files and directories.

If you don't have globbing enabled, do it by shopt -s globstar.

Note: A new globbing option works in Bash 4, zsh and similar shells.


Example:

$ mkdir -vp a/b/c/d
mkdir: created directory 'a'
mkdir: created directory 'a/b'
mkdir: created directory 'a/b/c'
mkdir: created directory 'a/b/c/d'
$ touch a/b/c/d/foo a/b/c/d/bar  a/b/c/d/.foo_hidden a/b/c/d/foo_not_hidden
$ echo **/*foo*
a/b/c/d/foo  a/b/c/d/foo_not_hidden
7
  • 5
    You don't need ls for that! echo **/*foo*
    – Kevin Cox
    Mar 1, 2016 at 3:25
  • @kenorb (and @kevin-cox). Could you be a bit more verbose? Why would globstar ( = * ) work here and how? What does the slash / do? May 18, 2017 at 19:47
  • @nuttyaboutnatty / means folder, it separates directories from the filename. * basically represents everything.
    – kenorb
    May 18, 2017 at 20:14
  • @kenorb yes-but: how does that one-liner help with the original question? May 19, 2017 at 14:17
  • 1
    @nuttyaboutnatty Clarified and added example. It helps by finding files ignoring the hidden one.
    – kenorb
    May 19, 2017 at 16:40
5
find $DIR -not -path '*/\.*' -type f \( ! -iname ".*" \)

Excludes all hidden directories, and hidden files under $DIR

1
  • This is the perfect answer. It recursively finds all files but excludes line items for directories and hidden files. Thanks!
    – KyleFarris
    Oct 8, 2015 at 5:41
2

The answer I originally posted as an "edit" to my original question above:

find . \( ! -regex '.*/\..*' \) -type f -name "whatever", works. The regex looks for "anything, then a slash, then a dot, then anything" (i.e. all hidden files and folders including their subfolders), and the "!" negates the regex.

2
  • Similar, but for only the current folder: find . \( ! -regex './\..*' \) -type f -maxdepth 1 -print
    – phyatt
    Mar 22, 2017 at 15:44
  • This fails if the start path contains dots in it such as find ../buildfiles <expressions>
    – John Rocha
    Mar 4 at 22:12
2

@Flimm's answer is good, particularly because it prevents find from descending into hidden directories. I prefer this simplification:

Generally to exclude all hidden paths (regular files, directories, etc):

find <start-point> -path '*/.*' -prune -o <expression> -print

For example, using your working directory as the start point, and -name '*some text*' as the expression:

find . -path '*/.*' -prune -o -name '*some text*' -print

In contrast to what @Flimm's answer suggests, no hidden files or hidden directories is the simple case. The -path '*/.*' expression is true for any path (regular files, directories, etc) that has a . immediately after your file separator, /. So this line will prune both hidden files and directories.

Allowing hidden files while excluding hidden directories is the case that requires a further filter. This is where you would include -type d in the the expression being pruned.

find <start-point> -type d -path '*/.*' -prune -o <expression> -print 

For example:

find . -type d -path '*/.*' -prune -o -name '*some text*' -print

In this case -type d -path '*/.*' is true only for directories, and so only directories are pruned.

2

This usually works too

find * [expression]

Using * shell wildcard for input all paths from working directory, usually * wildcard doesn't expand to hidden files but this could be changed in the shell options. Example:

find /path/* -iname file -mtime -3
1

A variant on https://askubuntu.com/a/749708/321070

find  -name '.?*' -prune -o -print

Instead of setting the directory to a full path, instead filter out things that mat ch .?* - a literal dot, any character, and then 0 or more characters.

3
  • Welcome to AskUbuntu, I tested this command and got everything listed. I kinda understand your idea but things need to improve a little. Aug 20, 2020 at 7:59
  • It works fine for me using find (GNU findutils) 4.7.0. Can you share a filename that is causing a false positive?
    – Neal Fultz
    Aug 20, 2020 at 16:08
  • My English sucks (or I am tired) and the question meant two cases, "ignore A and B". Second case, find "hidden files and hidden directories" yours is perfect. While reviewing your first post, I could only see your answer and the title of OP. Apologies. Aug 20, 2020 at 20:10
0

find has neat logic switches such as -and and -not you can use them to your advantage to find a matching file with two rules like so:

$ touch non_hidden_file.txt .hidden_file.txt somethings/.another_hidden_file.txt                                                 

$ find . -type f -name '*hidden_file*' -and \( -not -name ".*" \)                            
./non_hidden_file.txt

As you can see, find uses two rules -name '*hidden_file*' and -and \( -not -name ".*" \) to find those filenames that match both conditions - filename with hidden_file in it but without leading dot. Note the slashes in front of parenthesis - they are used to define parenthesis as find arguments rather defining a subshell (which is what parenthesis mean otherwise without slashes)

0
$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Notes:

  • . : current folder
  • remove -maxdepth 1 to search recursively
  • -type f : find files, not directories (d)
  • -not -path '*/\.*' : do not return .hidden_files
  • sed 's/^\.\///g' : remove the prepended ./ from the result list
0

This answer to a similar question, is the only one that gave me the same results I'd get if I went into each folder, and then did an ls. I had to add -type f for it to fully work.

find . -not -path '*/\.*' -type f

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.