I recently fell into the trap of believing that if I do a find
in bash
that prunes certain files and then finds only the remaining files (not directories), then the output would be those remaining files.
It didn't work as I expected.
Here's a simplified example. I've a directory structure as follows:
a
├── 1.log
├── 1.tgz
├── 1.txt
├── b
│ ├── 2.log
│ ├── 2.tgz
│ └── 2.txt
├── c
│ ├── 3.log
│ ├── 3.tgz
│ └── 3.txt
└── d
└── e
├── 4.log
├── 4.tgz
└── 4.txt
Let's say I want to find only the files that aren't *.log
or *.tgz
. (I realise all that's left is *.txt
files so there's an obvious way to find only those, but that's not always going to be the case, so please indulge me.)
My original command was:
find a \( -name '*.log' -o -name '*.tgz' \) -prune -o -type f
I expected this to work, but it listed all files:
a/1.log
a/b/2.tgz
a/b/2.txt
a/b/2.log
a/d/e/4.log
a/d/e/4.txt
a/d/e/4.tgz
a/1.txt
a/1.tgz
a/c/3.tgz
a/c/3.txt
a/c/3.log
According the the man
page for find
:
If the whole expression contains no actions other than -prune or -print, -print is performed on all files for which the whole expression is true.
To my mind, the whole expression is only true for the *.txt
files. If I do something simple like:
find a -type f
the -print
is implied according to the above, and all files are listed.
The thing that confused me is that if I add -print
to the end of my original command, it works as I would expect:
a/b/2.txt
a/d/e/4.txt
a/1.txt
a/c/3.txt
Since I kind of expected that -print
to be implied anyway, I wouldn't expect it to have any effect but clearly it does.
Could someone please explain how I should mentally parse such find
commands so that I don't fall into traps like this again?