7

I have a .bdf file inside subfolders. All the .bdf have the same name: Loads.bdf

Example:

/home/user/folderxx/folderxxx/Load.bdf
/home/user/folderxx/folderxxx/Load.bdf

I need to find (and copy to a new folder) all Load.bdf files and rename them with its path name.

Example: After the intended operation, it should show something like this.

folder001_folderAAA_Load.bdf
folder002_folderBBB_Load.bdf
10

I would use the mcp tool from the mmv ("multiple move") package for that:

mcp '/home/user/*/*/Load.bdf' '#1_#2_Load.bdf'

The #1, #2 etc. in the second string will be replaced by the contents of the corresponding 1st, 2nd etc. wildcard from the first string. Note that the path is in single quotes so that the wildcards are interpreted by mcp, not by the shell.

With the command shown, the files will be copied to the current directory. If you replace mcp with mmv, the files will be moved instead.

  • Double quoted strings also don't have wildcard expansion, at least in bash. – Daniel Beck Jun 17 '14 at 0:04
  • This was useful to me to copy the name of the folder to each file mmv '*/*.m4a' '#1.m4a' – Arturo Jul 1 '16 at 7:33
5

Using prename (aliased to rename on many systems) it is possible to do this without multiple runs of mv or cp:

cd /home/user
find . -type f -name 'Loads.bdf' -exec \
  prename -n 's:^./::; s:/:_:g; s:^:destination/:' {} +

Remove the -n when you are sure this is moving the files you want.

Update

To copy the files instead of moving them, the fastest way is to copy first with cpio or pax, then to rename as before. Eg (with GNU cpio):

cd /home/user
find . -type f -name 'Loads.bdf | cpio -p --make-directories destination

cd destination
find . -type f -name 'Loads.bdf' -exec  prename -n 's:^./::; s:/:_:g' {} +

Note that while pax is both POSIX and LSB and cpio is no longer POSIX, cpio is still the program which is commonly found many systems.

To handle filenames with newlines with GNU find/cpio it is possible to add -print0 to find and -0 to cpio.

Update 2

The pax solution is a one-liner (not including the cd):

cd /home/user
find . -name 'Loads.bdf' | cut -d/ -f 2- | pax -rws ':/:_:g' destination

Or for any filename (and GNU find):

find /home/user -name 'Loads.bdf' -printf '%P\0' | pax -0rws ':/:_:g' destination
  • There are two renames, the perl one (aka prename, but called rename by default in Ubuntu at least), and the other one that doesn't use regex (known as rename on Arch and Red Hat). Also, -execdir is preferred to -exec (see man find). Finally, doesn't this command move the files, not copy them? – Sparhawk Jun 16 '14 at 15:15
  • @Sparhawk, yes that's why I specified prename. Although I missed the 'copy` part in the question. – Graeme Jun 16 '14 at 15:17
  • Ah okay (was that an edit?). Perhaps I just read it wrong. – Sparhawk Jun 16 '14 at 15:17
  • @Sparhawk, no edit. Good call on the move vs copy though I have added an updated solution now. – Graeme Jun 16 '14 at 15:35
  • 1
    @Tim, updated with the pax solution, it has its own find/replace option. – Graeme Jun 16 '14 at 15:57
3

If you happen to need to do this on a system that doesn't have the tools mentioned in some of the other answers (like mcp and mmv, or prename), and you want to do it entirely in the shell, you can do something like:

#!/usr/bin/env bash

oldpath="/home/user"
newpath="/home/user/newfolder"

for file in $(find $oldpath -type f -name Load.bdf); do
        shortname=${file#$oldpath/}
        newname="$newpath/${shortname//\//_}"
        if [ -f $newname ]; then
                echo "$newname already exists."
        else
                echo "copy: $file"
                echo "  --> $newname"
                cp $file $newname
        fi
done

This script will find all the files named Load.bdf in the oldpath directory, munge the directory names into a new filename, and copy them to your destination directory. We can also check for the existence of files already copied, in case you need to run it more than once, and we don't need to copy all the files again.

The resulting directory structure would end up looking like:

bash$ find . -type f
/home/user/folder001/folderAAA/Load.bdf
/home/user/folder002/folderBBB/Load.bdf
/home/user/folder003/folderCCC/Load.bdf
/home/user/newfolder/folder001_folderAAA_Load.bdf
/home/user/newfolder/folder002_folderBBB_Load.bdf
/home/user/newfolder/folder003_folderCCC_Load.bdf
  • thank you all for your information. i got lot more information than what i need. thank you again. – sh999 Jun 16 '14 at 18:39
  • 1
    Awesome.. you could also outsource all the file existence logic to cp noclobber by dropping the whole if /else and using cp -n $file $newname – KeiferJ Oct 10 '17 at 15:47
1

In zsh, it's easy with zmv.

autoload -U zmv
cd /home/user
zmv '*/*/*' '${1//\//_}'
0
env - sh <<\SCRIPT

    t=/home/user/target f=folder
    cd "${t%/*}" && mkdir -p "$t"
    set -- $f??/$f???/Load.BDF
    while [ -e "$1" ] ; do
        cp "$1" "$t/$(IFS=/
            printf '%s_%s_%s\n' $1)"
    shift ; done

SCRIPT

I think this is all fully portable shell script - and it's pretty simply done, though I had my doubts at first.

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.