diff --git a/SC2146.md b/SC2146.md new file mode 100644 index 0000000..8a1b4ee --- /dev/null +++ b/SC2146.md @@ -0,0 +1,25 @@ +## This action ignores everything before the -o. Use \\( \\) to group. + +### Problematic code: + + find . -name '*.avi' -o -name '*.mkv' -exec cp {} /media \; + +### Correct code: + + find . \( -name '*.avi' -o -name '*.mkv' \) -exec cp {} /media \; + +### Rationale: + +In `find`, two predicates with no operator between them is considered a logical, short-circuiting AND (as if using `-a`). E.g., `-name '*.mkv' -exec ..` is the same as `-name '*.mkv' -a -exec ..`. + +`-a` has higher precedence than `-o`, so `-name '*.avi' -o -name '*.mkv' -a -exec ..` is equivalent to `-name '*.avi' -o \( -name '*.mkv' -a -exec .. \)`. + +In other words, the problematic code means "if name matches `*.avi`, do nothing. Otherwise, if it matches `*.mkv`, execute a command.". + +In the correct code, we use `\( \)` to group to get the evaluation order we want. The correct code means "if name matches `*.avi` or `*.mkv`, then execute a command", which was what was intended. + +### Contraindications + +If you're aware of this, you can either ignore this error or group to make it explicit. For example, to decompress all gz files except tar.gz, you can use: + + find . -name '*.tar.gz' -o \( -name '*.gz' -exec gzip -d {} + \)