I'm trying to copy a bunch of files named folder.jpg into a folder. The problem is because all the files are named the same thing, I need to rename them in the process. I know I can probably do it with sed but I'd like to rename them to the name of part of the parent folder. Here is what I got just to find and copy the files

cp $(find . -iname "folder.jpg") .albumart/

The folder structure is ./artist/artist.year.album/folder.jpg and what I'd like to use the parent folder (or just part of it) to name the file. Can someone help me with a one liner to accomplish the task? To make things even trickier, some folders have one more level of CD1 and CD2 that I would like to ignore if they are present (e.g. ./artist/artist.year.album/CD1/folder.jpg)

4 Answers 4


Assuming you have bash, this version simply takes your folder structure (e.g. ./foo/bar/baz/folder.jpg) and replaces all the slashes with underscores (e.g. so you get foo_bar_baz_folder.jpg):

find . -iname folder.jpg -exec bash -c 'for x; do x=${x#./}; cp -i "$x" ".albumart/${x//\//_}"; done' _ {} +

Note that no matter what you do, any time you move files from multiple locations into the same destination, there is always a chance of a name collision.

  • I believe this will move the .jpg files rather then copy is that correct? I think I'd just need to replace it with find . -iname folder.jpg -exec bash -c 'for x; do x=${x#./}; cp -i "$x" ".albumart/${x//\//_}"; done' _ {} + correct? Feb 4, 2013 at 21:17
  • @Rothgar Yes, I didn't read carefully enough and assumed you wanted to move. I've changed the mv to cp now.
    – jw013
    Feb 4, 2013 at 21:53

With zsh:

autoload zmv
zmv -n -C '(**/)folder.jpg' '.albumart/${1:t}.jpg'

Remove -n when happy.

That one will avoid name collisions (will fail with an error if two substitutions resolve to the same name) like:

$ zmv -n -C '(**/)folder.jpg' '.albumart/${1:t}.jpg'
zmv: error(s) in substitution:
b/c/folder.jpg and a/b/c/folder.jpg both map to .albumart/c.jpg
  • +1 for zmv. By far the easiest and whitespace-safe-est option for this sort of thing.
    – Kevin
    Feb 4, 2013 at 22:20
  • I like the idea. Any way to take out the spaces in the names (replace them with _)? I'm going to have to go find some more documentation on zsh and zmv for future reference. Feb 4, 2013 at 23:01

Don't parse the output of find. This breaks if there are whitespace or glob characters ?*\[ in file names. If you use find, make it call the command with the -exec action. Call a shell in the -exec action to perform the necessary string transformations.

find -name folder.jpg -exec sh -c '
    dir=${0%/*}      # stip off the file name
    case $dir in */CD[0-9]) dir=${dir%/*};; esac
    cp -ip "$0" ".albumart/${dir##*/}.jpg"
' {} \;

Zsh's zmv is often the easiest way to do this kind of copying.

zmv -C -o-p -w '**/*.jpg' '.albumart/${${${1%/}/CD<->}##*/}.jpg'

Some explanations:

  • The general syntax is zmv OPTIONS… SOURCE_PATTERN REPLACEMENT.
  • -w turns on wildcard group matching. In the replacement text, $1 refers to the part matched by **/ (with a final / unless the matched text is empty, i.e. the file is in the top directory), and $2 to the part matched by * in *.jpg.
  • Both the pattern and the replacement text must be protected against expansion: they need to be passed literally to zmv.
  • To understand ${${${1%/}/CD<->}##*/}, take it from inside out. First take $1. Strip off a final / (if any). Strip off a final pattern matching /CD<->, if any (<-> matches one or more digits). Finally off everything but the last directory component.
  • If you want to replace spaces by underscores, add one more layer of parameter expansion: ${${${${1%/}/CD<->}##*/}// /_}.

Obviously late with this answer, but hoping some others may find it useful.

for i in $(find . -type f -iname "folder.jpg" -printf "%P\n") ; do t=$(echo $i | sed 's|/|\.|g'); cp $i TARGETDIR/$t ; done

The -printf "%P\n" is to strip the leading ./ from find's output.

Echo find result ($i) into sed and make alterations, in this case sed replaces / with ., and assign the value of that whole thing to t

Then just copy the file found by find, $i in this case, to the TARGETDIR with new name $t.

Found: ./artist/artist.year.album/folder.jpg
Yield: TARGETDIR/artist.artist.year.album.folder.jpg

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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