8

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

5 Answers 5

11

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.

2
  • Double quoted strings also don't have wildcard expansion, at least in bash. Jun 17, 2014 at 0:04
  • This was useful to me to copy the name of the folder to each file mmv '*/*.m4a' '#1.m4a'
    – user96101
    Jul 1, 2016 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
7
  • 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, 2014 at 15:15
  • @Sparhawk, yes that's why I specified prename. Although I missed the 'copy` part in the question.
    – Graeme
    Jun 16, 2014 at 15:17
  • Ah okay (was that an edit?). Perhaps I just read it wrong.
    – Sparhawk
    Jun 16, 2014 at 15:17
  • @Sparhawk, no edit. Good call on the move vs copy though I have added an updated solution now.
    – Graeme
    Jun 16, 2014 at 15:35
  • 1
    @Tim, updated with the pax solution, it has its own find/replace option.
    – Graeme
    Jun 16, 2014 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
2
  • thank you all for your information. i got lot more information than what i need. thank you again.
    – sh999
    Jun 16, 2014 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, 2017 at 15:47
2

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .