views:

388

answers:

6

Trying to rename a series of files on a linux server. Finding the files I want is easy:

find . -type f -wholename \*.mbox

Of course, being mbox files, some of them have spaces in the names, so it becomes:

find . -type f -wholename \*.mbox -print0

I'm piping to xargs so that I can rename the files:

find . -type f -wholename \*.mbox -print0 | xargs -0 -I{} echo ${"{}"/.mbox/}

The echo should return something like INBOX, given INBOX.mbox, however, bash complains:

bash: ${"{}"/.mbox/}: bad substitution

How can I fix this? I'd like to try to keep it in a find/xargs solution if possible, so that I'm not adding a lot of looping constructs around it.

+1  A: 

You can try:

find . -type f -wholename \*.mbox -print0 | sed 's/\.mbox//'

Your problem is in trying to pipe into xargs. By that point "{}" doesn't mean anything.

To rename the files (Assuming you are under bash)

find . -type f -wholename \*.mbox -print0 | \
    while read I ; do
        mv $I $(echo $I | sed 's/\.mbox//') ;
    done ;
dsm
How will that help me rename the file though?
gms8994
edited the response to meet your needs
dsm
+4  A: 

Try

find . -type f -wholename \*.mbox | sed 's/\(.*\)\.mbox/mv "\1.mbox" "\1"/' | sh

This is not 100% fool proof should some of the files contain double quote characters, but I assume you can ignore that :)

hlovdal
An additional advantage to this solution is that if you leave off the `| sh` at the end you get a list of commands that will be executed. If everything looks ok, you can then pipe it to the shell to be executed.
Jon Ericson
Yes, good point. I always pipe to less first to see if the commands look sensible when running commands this way.
hlovdal
+1  A: 

I think You need this:

find . -wholename \*.mbox | awk '{new=$0; gsub("\.mbox$", "", new) ; system("mv \"" $0 "\" \"" new "\"") }'

(should be both gawk and mawk compatible, tested on mawk). It doesn't use xargs, but note that it doesn't fork a new process for every file moved. If You need to move a large amount of files, You'll notice the advantage. Warrning: if any of the files will contain a newline in it's name, You'll get in trouble (but if this is an issue, God be with You).

You further enhance this solution by using xargs to delete multiple files on a single rm invocation.

If You really need speed, I'd suggest that You use a python script instead of awk. That will do all the rm's without a single fork.

Reef
+1  A: 

Could you use rename(1)? It comes with perl.

Pasi Savolainen
A: 

Here you go, this should do the trick....

# find .
.
./test.tst
./test1.tst
./test2.tst
./test/test4.tst
./test/test5.tst

#

for file in `find . -type f`
do
mv $file ${file%.tst}.new
done

# find .
.
./test
./test/test4.new
./test/test5.new
./test1.new
./test2.new
guyver
this solution is prone to errors when files have spaces in them.
ghostdog74
A: 

GNU Parallel http://www.gnu.org/software/parallel/ has {.} that removes the extension:

find . -type f -wholename \*.mbox -print0 | parallel -0 mv {} {.}

If you know the filenames do not contain \n then this will work aswell:

find . -type f -wholename \*.mbox | parallel mv {} {.}
Ole Tange