Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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'm no bash expert but 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
done

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
done
share|improve this question
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 '13 at 16:21
    
Question edited. Still getting the same error. –  benbradley Jan 9 '13 at 17:28
1  
Remove the $i at the end... –  njsg Jan 9 '13 at 17:33

2 Answers 2

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

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$"
done
share|improve this answer
    
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. –  benbradley Jan 10 '13 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)
  total=${total%$'\t'*}
  echo "$total"$'\t'"$d"
done
share|improve this answer
    
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! –  benbradley Jan 10 '13 at 10:55
    
Also I did need -iname though as it was missing some directories containing .JPG/.JPEG files –  benbradley Jan 10 '13 at 11:05
    
Actually I just noticed one thing, the same directory often appears twice with different sizes reported –  benbradley Jan 10 '13 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). –  Gilles Jan 10 '13 at 13:14

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.