2

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

5

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.

2
  • 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? Commented 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
    Commented Feb 4, 2013 at 21:53
3

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
2
  • +1 for zmv. By far the easiest and whitespace-safe-est option for this sort of thing.
    – Kevin
    Commented 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. Commented Feb 4, 2013 at 23:01
1

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<->}##*/}// /_}.
0

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

You must log in to answer this question.

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