5

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)

4

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. ;)

  • +1: cool - I didn new about Uncode::Tussle kit. (In perl.deb package you get prename a not so unicoded variant of rename) – JJoao Apr 29 '15 at 9:49
  • 1
    Aside from being Unicode-aware, the great thing about Uncode::Tussle's rename is that it can use full Perl substitutions. It's a huge time saver in many situations. – lcd047 Apr 29 '15 at 10:02
  • I agree!!. prename is the same thing. (it is in face a 20 line very readable perl script) – JJoao Apr 29 '15 at 10:12
2

find is rarely useful when you don't need to traverse a directory tree recursively. Here a simple loop and shell wildcards are enough.

for x in */161901.pdf; do
  mv -- "$x" "xyz/${x%/*}.pdf"
done

Or, with the Perl-based rename command on Debian, Ubuntu and derivatives (prename on Arch, not the Linux rename command on other distributiosn):

rename 's!/.*!!; $_="xyz/$_.pdf"' */161901.pdf

Or, with zsh:

autoload -U zmv
zmv '(*)/161901.pdf' 'xyz/$1.pdf'
1

One way is to use find -print and pipe the filelist to awk where you can transform it to whatever you want, e.g. a cp command:

$ find -name 161901.pdf -print | awk -v TARGET=xyz -F'/' '{ printf "cp %s %s/%s.pdf\n", $0, TARGET, $2; }'
cp ./May-June-2011/161901.pdf xyz/May-June-2011.pdf
cp ./Nov-Dec-2011/161901.pdf xyz/Nov-Dec-2011.pdf

and this can then be piped to a shell to get executed:

find -name 161901.pdf -print | awk -v TARGET=xyz -F'/' '{ printf "cp %s %s/%s.pdf\n", $0, TARGET, $2; }' | sh 
  • I appreciate your use of awk! – Pandya Apr 29 '15 at 13:04
0

Sweet and simple:

#!/bin/bash

find . -name 161901.pdf | while read -r line
do 
  # this will create new filenames e.g. ./May-2013/161901.pdf will be now May-2013-161901.pdf
  filename=$(echo $line | grep -oP "\/\K.+$" | sed 's/\//-/g')
  # now just move the files
  mv $line xyz/$filename
done

So, essentially it's just a 2-step process. Create new filenames and move 'em.

  • Thank-you for reminding while read -r line! – Pandya Apr 29 '15 at 13:08
0

If you have rename / prename command

find -name 161901.pdf -exec prename -n 's!/!-!g; s!\W*!xyz/!' {} +

and remove -n if you like what you see.

(Larry Wall and Robin Barker' prename is installed with perl.deb package; Brian d Foy Unicode::Tussle rename also works)

0

Later I found while read -r line very helpful and I've succeed with following command:-

find -name 161901.pdf | while read -r line; do mv $line ./xyz/$(echo $line | cut -d "/" -f 2).pdf; done

Explanation:-

  • Here find -name 161901.pdf list the founded files (already mentioned in question) which piped to while loop in while path stored in variable line.
  • cut -d "/" -f 2 filters the directory name e.g-May-June-2011
  • And finally mv $line ./xyz/$(echo $line | cut -d "/" -f 2).pdf moves files (with renamed to directory name in which they were)

So, find+whileloop with mv using cut for naming accomplishes what I want.

0

This could be a possible solution. The argument of the script is the directory in which you are searching, and now it is just printing what will do:

#!/bin/bash

find "$1" -type f -name '161901.pdf' -print0 | while IFS= read -r -d '' f
do
    # get folder name
    d="$(basename "${f%/*}")"
    target="${d}.pdf"
    echo cp $f "xyz/${target}"
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.