Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I am trying to rename files in sub directories using xargs by running the following command:

find . -name oldfile.txt -print0 | xargs -n 1 -0 -I % mv % newfile.txt

The problem is that the file gets moved into the directory I run the command in instead of the directory that oldfile.txt is found in. I cant seem the solution for this, any ideas?

I'm thinking that perhaps I can get the directory from the found file's path and then append it to the front of the second paramater of mv but how would that work?

share|improve this question

1 Answer 1

up vote 2 down vote accepted

Instead of relying on external commands, where you lose the information about the current directory, you can use find's -execdir option to have it execute commands in the directory a file was found in.

find . -name oldfile.txt -execdir mv -- {} newfile.txt \;

This is actually a more secure version of -exec that is preferred in most situations. The only drawback is that it's not specified by POSIX, so you won't find it in all find variants – but at least in GNU find and BSD find you can.

If you want to be extra safe, you should call mv like mv -- source target so that a file starting with a dash isn't incorrectly interpreted as an argument to mv.


If for some reason you don't want to use -execdir or have other complicated things to do, or just want to learn a little about Bash, here's how to get the folder and move the file based on that:

find . -name oldfile.txt -exec bash -c 'mv -- "${0}" "$(dirname $0)"/newfile.txt' {} \;

Here, {} passes the full path to bash as $0. We get its directory name through command substitution ($(…)) and then append the new name. Both the original argument and the directory name have to be double quoted to prevent whitespace from breaking the command – otherwise mv would think you're trying to move two files called foo and bar when the file is actually called foo bar, for example.

share|improve this answer
    
This worked! Excellent solution. The third paramater of mv is the directory? I've never seen mv have a third param. I looked at the manual but it doesn't seem clear. –  Chris B May 17 '13 at 19:28
    
No, it has nothing to do with mv. The \; is part of the -exec / -execdir option – see the find manual I linked you to. You always have to include it in order to have find recognize the end of the command specified in exec. –  slhck May 17 '13 at 19:29
    
Ah I see! Both of these constructions need to be escaped (with a ‘\’) or quoted to protect them from expansion by the shell. That means that {} should be escaped as well. –  Chris B May 17 '13 at 19:31
    
You don't really have to escape or quote {}, it also works without that in most shells or find versions. Quoting {} is only necessary when it would be interpreted otherwise by the shell, which usually isn't the case. I think @DanielBeck recently had a problem with the fish shell and that – but I'm not entirely sure if fish was the whole cause of the issue. –  slhck May 17 '13 at 19:37
    
Added another example of how to achieve what you need, albeit a little more complicated than it needs to be. –  slhck May 17 '13 at 19:44

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.