Find files based on name and moving them with renaming simultaneously

command linefindmvrename

Consider I've directories named according to years and containing pdf files according to subject-code.

ls output is:

$ ls -l
total 32
drwxrwxrwx 1 root root 4096 Apr  8 08:52 May-June-2011
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Nov-Dec-2011
drwxrwxrwx 1 root root 4096 Apr  8 20:36 Summer-2012
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Summer-2013
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Summer-2014
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2012
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2013
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2014

Each directories contains pdf files according to subject-code:-

ls -l May-June-2011/
total 808
-rwxrwxrwx 1 root root 104193 May  1  2011 161901.pdf
-rwxrwxrwx 1 root root 103380 May  1  2011 161902.pdf
-rwxrwxrwx 1 root root 115664 May  1  2011 161903.pdf
-rwxrwxrwx 1 root root  88953 May  1  2011 161904.pdf
-rwxrwxrwx 1 root root 179268 May  1  2011 161905.pdf
-rwxrwxrwx 1 root root 116158 May 24  2011 161906.pdf
-rwxrwxrwx 1 root root 106033 May  1  2011 161907.pdf

In other words each directory has 16190{1..7}.pdf


Now suppose I want to move all (from all directory mentioned) 161901.pdf to a specific directory (say xyz) with renaming to it's parent folder's name.pdf.

Explanation :-

Here is list of all 161901.pdf:

$ find -name 161901.pdf
./May-June-2011/161901.pdf
./Nov-Dec-2011/161901.pdf
./Summer-2012/161901.pdf
./Summer-2013/161901.pdf
./Summer-2014/161901.pdf
./Winter-2012/161901.pdf
./Winter-2013/161901.pdf
./Winter-2014/161901.pdf

I want that ./May-June-2011/161901.pdf should be moved into xyz with new name May-June-2011.pdf (name of directory inside which file is). i.e move ./May-June-2011/161901.pdf to ./xyz/May-June-2011.pdf

Similarly ./Nov-Dec-2011/161901.pdf to ./xyz/Nov-Dec-2011.pdf, ./Summer-2012/161901.pdf to ./xyz/Summer-2012.pdf and so on (up to ./Winter-2014/161901.pdf to ./xyz/Winter-2014.pdf).

The expected output for ls xyz is:

$ls xyz
May-June-2011.pdf
Nov-Dec-2011.pdf
Summer-2012.pdf
Summer-2013.pdf
Summer-2014.pdf
Winter-2012.pdf
Winter-2013.pdf
Winter-2014.pdf

How can I accomplish this? (how to find -exec or with loop or something else)

Best Answer

Perl module Unicode::Tussle comes with a very useful script named rename (which is unfortunate, because the name clashes with the standard rename(1) on Linux). With it, you could do something like this:

mkdir xyz
find . -name '*.pdf' -print0 | \
    rename -0 's!^\.!xyz!;  s!/[^/]*\.pdf$!.pdf!'

Without Perl, you could still do the same thing with a bit of shell plumbing:

mkdir xyz
find . -name '*.pdf' | sed 's/^/mv /' >src
find . -name '*.pdf' | sed 's!^\./!xyz/!; s!/[^/]*\.pdf$!.pdf!' >dest
join src dest | sh -
rm -f src dest

And, as I'm sure other people will be quick to point out, you can even put it on a single line, using <(...) process substitution. I'm not sure that would make things more legible though. Computers these days are powerful enough to allow a more verbose syntax. ;)

Related Question