1

I have spent almost one day and a half searching for this but couldn't find an answer.

I am looking to add the folder's name as a prefix to all files ending with .bin in that folder.

So, if the folder's name is ABC and it contains the files aa.bin, bb.bin, cc.bin, etc. then I need them to be renamed as ABC-aa.bin, ABC-bb.bin, ABC-cc.bin, etc.

Also, this folder ABC has subfolders, let's call them: ABC1, ABC2, ABC3, etc. which also have .bin files. For example ABC1 has aa1.bin, bb1.bin, cc1.bin, etc.

I need that command to go through all folders and append that sub-folder's name to files inside it too. So, aa1.bin will be ABC1-aa1.bin, and so on.

Directory structure:

.
└── ABC
    ├── aa.bin
    ├── ABC1
    │   ├── aa1.bin
    │   └── bb1.bin
    ├── ABC2
    │   ├── aa2.bin
    │   └── bb2.bin
    ├── bb.bin
    └── cc.bin

3 Answers 3

1

Here's a "one-liner" to do, using shell built-ins to manipulate the filenames:

find . -name '*.bin' -exec sh -c 'p="${1%/*}"; f="${1##*/}"; echo mv -- "$1" "$p/${p##*/}-$f"' sh {} \;

Test it in the target directory and remove the echo if the results look correct

e.g. given

$ tree .
.
└── ABC
    ├── aa.bin
    ├── ABC1
    │   ├── aa1.bin
    │   └── bb1.bin
    ├── ABC2
    │   ├── aa2.bin
    │   └── bb2.bin
    ├── bb.bin
    └── cc.bin

3 directories, 7 files

then

$ find . -name '*.bin' -exec sh -c 'p="${1%/*}"; f="${1##*/}"; echo mv -- "$1" "$p/${p##*/}-$f"' sh {} \;
mv -- ./ABC/ABC2/bb2.bin ./ABC/ABC2/ABC2-bb2.bin
mv -- ./ABC/ABC2/aa2.bin ./ABC/ABC2/ABC2-aa2.bin
mv -- ./ABC/aa.bin ./ABC/ABC-aa.bin
mv -- ./ABC/bb.bin ./ABC/ABC-bb.bin
mv -- ./ABC/ABC1/bb1.bin ./ABC/ABC1/ABC1-bb1.bin
mv -- ./ABC/ABC1/aa1.bin ./ABC/ABC1/ABC1-aa1.bin
mv -- ./ABC/cc.bin ./ABC/ABC-cc.bin

looks good, so remove the echo and run it again

$ find . -name '*.bin' -exec sh -c 'p="${1%/*}"; f="${1##*/}"; mv -- "$1" "$p/${p##*/}-$f"' sh {} \;

giving

$ tree .
.
└── ABC
    ├── ABC1
    │   ├── ABC1-aa1.bin
    │   └── ABC1-bb1.bin
    ├── ABC2
    │   ├── ABC2-aa2.bin
    │   └── ABC2-bb2.bin
    ├── ABC-aa.bin
    ├── ABC-bb.bin
    └── ABC-cc.bin

3 directories, 7 files
4
  • Technically find is /usr/bin/find :)
    – kos
    Feb 8, 2016 at 18:27
  • Somehow this isn't working.. filename doesn't changes. This is what I get: mv -- ./p6modem.bin ./.-p6modem.bin mv -- ./p7/p7modem.bin ./p7/p7-p7modem.bin See, no changes to filename are being made. Using the command as it is BTW.
    – Krishna
    Feb 8, 2016 at 18:35
  • @Krishna did you remove the echo command? @Kos sorry by "it" I meant the slicing-'n'-dicing of the filenames - not the find part Feb 8, 2016 at 19:21
  • Works 100% right out of the box!! Thanks a lot!
    – Krishna
    Feb 10, 2016 at 10:32
1

You can do this by using find, basename, dirname and mv commands. For example like in this simple script:

#!/bin/bash

for f in $(find ABC -name '*.bin'); 
do 
  filename="$(basename $f)" 
  dir="$(dirname $f)"
  lastdir="$(basename $dir)"
  newname="$lastdir-$filename"
  newpath="$dir/$newname"
  mv $f $newpath
done

Here I'm using find to recursively find all .bin files in ABC, then iterate over them and extracting the last folder name. Then the new path is constructed and the file is moved to the same directory but with the new name.

Edit:
In order to execute this script copy its contents to some file. Let's say the filename is renameFiles.sh. Then make it executable:

chmod +x renameFiles.sh

After this you can execute it with ./renameFiles.sh. Notice that the folder where you put this file should be the same where your ABC folder is located.

renameFiles.sh
ABC
  aa.bin
  bb.bin
  ABC1
    aa1.bin
    etc.
3
  • I am totally new to Linux. Don't know how to use 'for' thing .. sorry if you feel upset about this... When I enter !/bin/bash in terminal, I only get error... (event not found).. looking to fix it, BTW.
    – Krishna
    Feb 8, 2016 at 17:16
  • I updated the answer so hopefully its clear now. Feb 8, 2016 at 17:33
  • 1
    I researched on it and had found out. Exactly how you are guiding. Did that and it's working alright now. Thanks a ton!!
    – Krishna
    Feb 8, 2016 at 17:50
0

Using rename:

shopt -s globstar
rename -n '(! -f $_ || ! /.bin$/) && next; $" = "/"; my @f = split("/"); $f[@f-1] =~ s/^/$f[@f-2]-/; $_ = "@f"' **
  • shopt -s globstar: If set, the pattern '**' used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a '/', only directories and subdirectories match.
  • rename -n '! -f $_ && next; $" = "/"; my @f = split("/"); $f[@f-1] =~ s/^/$f[@f-2]-/; $_ = "@f"' **: performs a dry-run of the following. For each filename resulting from the ** globbing: if the file is not a regular file or the filename doesn't end with .bin, skips to the next filename; sets the output field separator to /; splits the filename on /; prepends the second to last field followed by - to the last field; assigns the new filename to the default variable ($_).

If the result is the expected one, remove the -n option from the rename command.

$ tree
.
└── ABC
    ├── aa.bin
    ├── ABC1
    │   ├── aa1.bin
    │   └── bb1.bin
    ├── ABC2
    │   ├── aa2.bin
    │   └── bb2.bin
    ├── bb.bin
    └── cc.bin

3 directories, 7 files
$ rename -n '(! -f $_ || ! /.bin$/) && next; $" = "/"; my @f = split("/"); $f[@f-1] =~ s/^/$f[@f-2]-/; $_ = "@f"' **
Exiting subroutine via next at (eval 4) line 1.
rename(ABC/aa.bin, ABC/ABC-aa.bin)
Exiting subroutine via next at (eval 4) line 1.
rename(ABC/ABC1/aa1.bin, ABC/ABC1/ABC1-aa1.bin)
rename(ABC/ABC1/bb1.bin, ABC/ABC1/ABC1-bb1.bin)
Exiting subroutine via next at (eval 4) line 1.
rename(ABC/ABC2/aa2.bin, ABC/ABC2/ABC2-aa2.bin)
rename(ABC/ABC2/bb2.bin, ABC/ABC2/ABC2-bb2.bin)
rename(ABC/bb.bin, ABC/ABC-bb.bin)
rename(ABC/cc.bin, ABC/ABC-cc.bin)
2
  • It's a wonderful command.. but how to restrict it to .bin files only? Thanks! That it (already) excludes directories and sub-directories too is good.
    – Krishna
    Feb 8, 2016 at 18:42
  • @user3647217 Fair enough, didn't think that would have renamed even files not ending with .bin, edited the answer. Try this instead: rename -n '(! -f $_ || ! /.bin$/) && next; $" = "/"; my @f = split("/"); $f[@f-1] =~ s/^/$f[@f-2]-/; $_ = "@f"' **.
    – kos
    Feb 8, 2016 at 18:49

You must log in to answer this question.

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