1

I wanted to recursively run chmod go+w in a particular folder including hidden files, and I first tried

find . -name ".*" -o -name "*" -exec chmod go+w {} \;

but I found that it wasn't affecting hidden files. To check myself, I ran just

find . -name ".*" -o -name "*"

and the hidden files were listed. I also noticed that if I excluded the -o -name "*" part it would chmod the hidden files (but exclude non-hidden of course). My last attempt was to use xargs instead

find . -name ".*" -o -name "*" | xargs chmod go+w

which finally worked as expected. What am I doing wrong in the first snippet?

Red Hat Enterprise Linux Server release 6.8 (Santiago)

GNU bash, version 4.3.42(1)-release (x86_64-unknown-linux-gnu)

Brent
  • 214
  • 2
  • 8
  • 1
    Apart from `find` , `chmod` with `-R, --recursive` could also be used right, like `chmod -R go+w ` ? – ss_iwe May 25 '17 at 06:24
  • 2
    As someone put it, the key to understanding `find` is to understand that it doesn't find files, it evaluates expressions. It also doesn't really have distinct concepts of "tests" and "actions", they are all operations on the same level. – ilkkachu May 25 '17 at 07:02
  • `find` locates and reports dot-files by default... – Kevin Jun 22 '17 at 20:49
  • Not in my experience – Brent Jun 22 '17 at 20:55

1 Answers1

4

The solution is to bind the two name tests together with parens.

To illustrate this, let's consider a directory with three regular files:

$ ls -a
.  ..  .hidden1  .hidden2  not_hidden

Now, let's the original command:

$ find . -name ".*" -o -name "*" -exec echo Found {} \;
Found ./not_hidden

Only the non-hidden file is found.

Next, let's add parens to group the two name tests together:

$ find . \( -name ".*" -o -name "*" \) -exec echo Found {} \;
Found .
Found ./not_hidden
Found ./.hidden1
Found ./.hidden2

All files are found.

The solution is to use parens.

More details

In the original command, there is not operator between -name "*" and -exec ... \;. Consequently, find assumes the default operator which is logical-and. Because logical-and binds tighter than logical-or (-o), this means that the command is interpreted as:

find . \( -name ".*" \) -o \( -name "*" -exec echo Found {} \; \)

This means that the exec is run only if the first name condition failed to match.

For more information, see the OPERATORS section in man find.

What happens without -exec

Let's try using a simple -print:

$ find . -name ".*" -o -name "*" -print
./not_hidden

As you can see, -print bound to the -name "*" with the implicit logical-and as above.

But, consider what happens without any action specified:

$ find . -name ".*" -o -name "*"
.
./not_hidden
./.hidden1
./.hidden2

Here, all files were found. The reason is that, in this version, -o is the only operator.

John1024
  • 73,527
  • 11
  • 167
  • 163