I'm trying to get a per-directory total size of all the .jpg/.jpeg images in each directory that contains such images. And showing the full directory path.

I've managed to cobble this together from various bits I've found.

for i in $(tree -dfi --noreport); do
  find . \( -iname "*.jpg" -or -iname "*.jpeg" \) -type f -exec du -c {} \; $i

However I'm getting an error:

find: paths must precede expression

Anyone know what I've done wrong?
Or can suggest any alternatives with bash that might do what I'm looking for?

I get the same error when changing it to this:

for i in $(tree -dfi --noreport); do
  find $i \( -iname "*.jpg" -or -iname "*.jpeg" \) -type f -exec du {} \; $i
  • 2
    Is $i the directory where you want to search for jp[e]g images, that is, the path? If so, "[the] path[s] must precede the expression" (the bit where you put the -inames). Why do you have a . after find instead of $i?
    – njsg
    Jan 9, 2013 at 16:21
  • Question edited. Still getting the same error.
    – batfastad
    Jan 9, 2013 at 17:28
  • 1
    Remove the $i at the end...
    – njsg
    Jan 9, 2013 at 17:33

2 Answers 2

for i in $(tree -dfi --noreport); do
    find $i -type f \( -iname "*.jpg" -or -iname "*.jpeg" \) -exec du {} \;

Drop the path name at the end of the find command and -type option should appear before any other options to make search a bit faster. This should do it.

By the way, to help you a bit, I would have done this in this way:

for i in $( tree -dfi --noreport ); do 
    find $i -maxdepth 1 -type f \( -iname "*.jpg" -or -iname "*.jpeg" \) -exec du '{}' \; | awk -v d=$i '{ j+=$1; } END{ printf("%s: %d\n", d, j) }' | grep -Ev ": 0$"
  • Thanks for that. Though it doesn't do what I was hoping. Also I think it borks on dir names containing spaces. A fault in my original approach.
    – batfastad
    Jan 10, 2013 at 10:58

Your immediate error is that extra $i at the end of the find invocation — just remove it. The order of arguments for find is first the directories to traverse, then the expression to match.

I don't get the point of the call to tree: find can do this. With GNU find (i.e. on Linux or Cygwin), assuming your directories don't contain insanely many .jpg files, the -execdir primary on find lets you run a command on all the matching files in a directory.

find . \( -name '*.jpg' -o -name '*.jpeg' \) -execdir sh -c 'echo "$(du -c "$@" | sed -n "\$s/\\t.*//p") ${PWD#$0/}"' $PWD {} +

Note that versions of GNU find prior to 4.5.9 have a bug that causes -execdir … {} + to run one command per file, which is no good here. So you may have to work harder.

You can traverse the directory tree in bash. Set the globstar option to enable the pattern **/, which matches any number of subdirectory levels, i.e. it enumerates subdirectories recursively. In each subdirectory, if there are JPEG files, call du to compute their total size.

shopt -s globstar
for d in **/*/; do
  files=("$d/"*.jpg "$d/"*.jpeg)
  total=$(du -s -- "$files" 2>/dev/null | tail -n 1)
  echo "$total"$'\t'"$d"
  • Nice one... that one liner works beautifully. Nice tip on -execdir as well. Also seems to work with dir names containing spaces. I'm only just getting started with bash but I love how there are so many ways to do things like this. I couldn't find a way to do this under Windows so mounted the SMB share and ran this command. Nice!
    – batfastad
    Jan 10, 2013 at 10:55
  • Also I did need -iname though as it was missing some directories containing .JPG/.JPEG files
    – batfastad
    Jan 10, 2013 at 11:05
  • Actually I just noticed one thing, the same directory often appears twice with different sizes reported
    – batfastad
    Jan 10, 2013 at 13:06
  • @benbradley The -execdir one-liner unfortunately only works on very recent (and IIRC a few older versions) of GNU find. I think most currently deployed systems have a buggy version where this reports every file independently. Try the bash loop (beware that I haven't tested it). Jan 10, 2013 at 13:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .