0

I'm trying to use the find command to list the size of a certain set of files but am getting no output. The command I use is:

find POD -type f -name *.mp3 -or -name *.ogg -ls

Which produces no output. While:

find POD -type f -name *.mp3 -or -name *.ogg

does produce output, I've also tried the actions:

-printf "%p %k KB\n"
-exec ls -ls '{}' \;
-print0

but all of these give no output. When I use any of these actions with a different expression e.g.:

find . -maxdepth 1 -type f -printf "%p %k KB\n"

I also get the expected output. Does anybody have any idea what the problem is? I'm running:

Linux irimi 3.10.37-1-MANJARO #1 SMP Mon Apr 14 20:56:29 UTC 2014 x86_64 GNU/Linux

aka an up to date Manjaro linux distribution. The shell I use is: /bin/bash version 4.3.8(1)-release.

The content of my SHELLOPTS environment variable is:

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

my BASHOPTS environment variable is:

cdspell:checkwinsize:cmdhist:complete_fullquote:dotglob:expand_aliases:extglob:extquote:force_fignore:histappend:hostcomplete:interactive_comments:nocaseglob:progcomp:promptvars:sourcepath

Again any help on trying to debug this would be very much appreciated.

2 Answers2

1

There is a trap with the and/or keywords on find. or applied to all the following parameters including the action (-ls in your example). and expressions without or (or attached with and) are evaluated in reading order with a final stop as false. There is no implicit ().

So the command find POD -type f -name *.mp3 -or -name *.ogg -ls means,

  • search (starting from the POD directory) for files --- if no file found : STOP
  • else (file found) check pattern matching *.mp3 --- if pattern match : STOP ! (because OR applied from here and only if the previous command failed (but only the previous command, not the previous groups of command)

and because you add an execute statement (-ls, -exec, -print....) in the command line there is no implicit -print command and so nothing to execute if 1) all the conditions of 1) are true (file and pattern matched). If you remove the last -ls there an implicit -print distributed at the end of each condition branches.

  • else if pattern didn't match search for anything (file/directory) matching the pattern *.ogg and list them (the -ls is not a condition command, it's executed only if the previous command/test "pattern *.ogg is true). But because of 1), 2) is evaluated only for NON mp3 files. If you don't have .ogg files you don't see anything.

Solution 1 repeat the execution command in each logical branch

   find POD -type f -name "*.mp3" -ls -or -name "*.ogg" -ls

Solution 2 add (shell protected) parenthesis

   find POD -type f \( -name "*.mp3" -ls -or -name "*.ogg" \) -ls

Note you should protect the patterns to avoid shell pattern evaluation in the current directory.

Ramesh
  • 39,297
0

I think it's because of the evaluation order (lack of explicit precedence) e.g. if

-name '*.mp3' -o -name '*.ogg' -ls

then if -name '*.ogg' evaluates false, the ls action is not executed. You can get the behaviour you expect by grouping your OR expressions using parentheses - for example, if

$ ls tests
file1.mp3  file2.mp3  file3.mp3

Then

$ find tests \( -name '*.mp3' -o -name '*.ogg' \) -print
tests/file3.mp3
tests/file1.mp3
tests/file2.mp3

whereas

$ find tests -name '*.mp3' -o -name '*.ogg' -print

produces no output. Note that

$ find tests -name '*.mp3' -o -name '*.ogg'

is a special case because it is implicitly treated as

$ find tests \( -name '*.mp3' -o -name '*.ogg' \) -print

Also note that it's good practice to quote or escape shell globs in a find command to prevent the shell from expanding them - see the NON-BUGS section of the find manpage.

steeldriver
  • 81,074