How do I use find to copy all found files to a new name in their same directories?

4

2

I've got a simple command that does almost what I want. The following will locate all files with a suffix of '_compressed.swf' and copy each into its same directory with a '.bak2' appended:

find '../content' -name '*_compressed.swf' -print0 | xargs -0 -I {} cp {} {}.bak2

Results
In: /content/somefile_compressed.swf
Out: /content/somefile_compressed.swf.bak2

However, I need to replace '_compressed.swf' with '_content.swf' I'd like to use find, rather than recursive flag on cp for consistency.

Objective
In: /content/somefile_compressed.swf
Out: /content/somefile_content.swf

Michael Prescott

Posted 2010-02-23T17:22:28.267

Reputation: 3 351

1

Cross-posted here: http://serverfault.com/questions/115880/how-do-i-use-find-to-copy-all-found-files-to-a-new-name-in-their-same-directories

– Paused until further notice. – 2010-02-23T18:38:30.193

Answers

4

This solution is probably the most portable:

find "../content" -name "*_compressed.swf" -exec sh -c 'cp {} `dirname {}`/`basename {} compressed.swf`content.swf' \;

There is also the famous rename.pl script which is distributed with Perl, and the rename command which could have made this a bit easier. These aren't available on all distributions though, these commands are for the most part.

John T

Posted 2010-02-23T17:22:28.267

Reputation: 149 037

The above moves the files from their current location to the script's location and adds the suffix 'content.swf'. Replacing mv with cp gets me closer, but I still need replace text in name rather than append. I'm getting closer by using sed, but still learning. – Michael Prescott – 2010-02-23T22:00:14.390

@Michael you're right! whoops. I was only testing in the current directory hence why it worked for me. I've fixed it :) – John T – 2010-02-24T04:19:26.717

Hey, thanks John. I like it. Much of this is all new to me. I understand usage of find, -exec, sh, dirname {}, basename {}, but I'm a little confused about the combined usage. sh -c ' ' converts the string into a command and runs the command, right? mv {} expands into one of find results "mv ../content/file1of20_compressed.swf ???????" It's the second part of that which I don't understand yet. What is causing the compressed.swf to be replaced by content.swf? In my solution you can see I just learned how to use sed. – Michael Prescott – 2010-02-24T17:53:02.553

2@Michael what's happening is the file is being moved with a new name. If {} contained /etc/somefile_compressed.swf, dirname takes /etc and basename {} compressed.swf will leave you with somefile_. Now you put these 2 together with the slash I added in the middle (/) and you have /etc/somefile_. You'll notice I added content.swf to the end so it completes the statement as /etc/somefile_content.swf. HTH. – John T – 2010-02-24T22:54:59.753

i'd use rename.pl if mv functionality was desired (it's not hard to add to any system with Perl) but the cp makes this a different beast. you'd have to tweak the script to do a copy instead of rename, which would work, but is more work. @John - nice find solution; it's big and long and ugly, but it looks like it would work. (don't ask me to help test it tho!) :) – quack quixote – 2010-02-24T23:45:16.777

@~quack I totally missed out on the cp part! fixed, thanks :) – John T – 2010-02-24T23:48:04.337

1

I'm sure this all can be handled in one line. I got really close to a one line solution and with some help even closer, but no success. The following works:

#!/bin/bash

# Read all file names into an array
FilesArray=($(find "../content" -name "*_compressed.swf"))

# Get length of an array
FilesIndex=${#FilesArray[@]}


# Use for loop read all directory names
for (( i=0; i<${FilesIndex}; i++ ));
do
    source="${FilesArray[$i]}"
    destination="$(echo "${source}" | sed 's/compressed/content/')"
    cp "${source}" "${destination}"
done

exit 0;

Michael Prescott

Posted 2010-02-23T17:22:28.267

Reputation: 3 351

0

I believe this should do the trick and is collapsible to a one liner if you really really need to ;)

find ../content -name '*_compressed.swf' | while read file
do
cp "$file" "${file/_compressed.swf/_content.swf}"
done

Bash pattern substitution works nicely in these cases.

jonathanserafini

Posted 2010-02-23T17:22:28.267

Reputation: 2 464