gpow
gpow

Reputation: 751

bash script to rename all files in a directory?

i have bunch of files that needs to be renamed.

file1.txt needs to be renamed to file1_file1.txt
file2.avi needs to be renamed to file2_file2.avi

as you can see i need the _ folowed by the original file name.

there are lot of these files.

Upvotes: 6

Views: 25973

Answers (8)

ghostdog74
ghostdog74

Reputation: 342273

for file in file*.*
do 
    [ -f "$file" ] && echo mv "$file" "${file%%.*}_$file"
done

Idea for recursion

recurse() {
 for file in "$1"/*;do
    if [ -d "$file" ];then
        recurse "$file"
    else
        # check for relevant files
        # echo mv "$file" "${file%%.*}_$file"
    fi
 done
}
recurse /path/to/files

Upvotes: 4

SiegeX
SiegeX

Reputation: 140227

So far all the answers given either:

  1. Require some non-portable tool
  2. Break horribly with filenames containing spaces or newlines
  3. Is not recursive, i.e. does not descend into sub-directories

These two scripts solve all of those problems.

Bash 2.X/3.X

#!/bin/bash

while IFS= read -r -d $'\0' file; do
    dirname="${file%/*}/"
    basename="${file:${#dirname}}"
    echo mv "$file" "$dirname${basename%.*}_$basename"
done < <(find . -type f -print0)

Bash 4.X

#!/bin/bash

shopt -s globstar
for file in ./**; do 
    if [[ -f "$file" ]]; then
        dirname="${file%/*}/"
        basename="${file:${#dirname}}"
        echo mv "$file" "$dirname${basename%.*}_$basename"
    fi
done

Be sure to remove the echo from whichever script you choose once you are satisfied with it's output and run it again

Edit

Fixed problem in previous version that did not properly handle path names.

Upvotes: 7

James Morris
James Morris

Reputation: 4935

#!/bin/bash
# Don't do this like I did:
# files=`ls ${1}`

for file in *.*
do
if [ -f $file ];
then
newname=${file%%.*}_${file}
mv $file $newname
fi
done

This one won't rename sub directories, only regular files.

Upvotes: -1

paxdiablo
paxdiablo

Reputation: 881093

For your specific case, you want to use mmv as follows:

pax> ll
total 0
drwxr-xr-x+ 2 allachan None 0 Dec 24 09:47 .
drwxrwxrwx+ 5 allachan None 0 Dec 24 09:39 ..
-rw-r--r--  1 allachan None 0 Dec 24 09:39 file1.txt
-rw-r--r--  1 allachan None 0 Dec 24 09:39 file2.avi

pax> mmv '*.*' '#1_#1.#2'

pax> ll
total 0
drwxr-xr-x+ 2 allachan None 0 Dec 24 09:47 .
drwxrwxrwx+ 5 allachan None 0 Dec 24 09:39 ..
-rw-r--r--  1 allachan None 0 Dec 24 09:39 file1_file1.txt
-rw-r--r--  1 allachan None 0 Dec 24 09:39 file2_file2.avi

You need to be aware that the wildcard matching is not greedy. That means that the file a.b.txt will be turned into a_a.b.txt, not a.b_a.b.txt.

The mmv program was installed as part of my CygWin but I had to

sudo apt-get install mmv

on my Ubuntu box to get it down. If it's not in you standard distribution, whatever package manager you're using will hopefully have it available.

If, for some reason, you're not permitted to install it, you'll have to use one of the other bash for-loop-type solutions shown in the other answers. I prefer the terseness of mmv myself but you may not have the option.

Upvotes: 5

JAL
JAL

Reputation: 21563

I use prename (perl based), which is included in various linux distributions. It works with regular expressions, so to say change all img_x.jpg to IMAGE_x.jpg you'd do

prename 's/img_/IMAGE_/' img*jpg

You can use the -n flag to preview changes without making any actual changes.

prename man entry

Upvotes: 0

Carl Smotricz
Carl Smotricz

Reputation: 67750

One should mention the mmv tool, which is especially made for this.

It's described here: http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/mass-rename.html

...along with alternatives.

Upvotes: 0

Rob Curtis
Rob Curtis

Reputation: 405

find . -type f | while read FN; do
  BFN=$(basename "$FN")
  NFN=${BFN%.*}_${BFN}
  echo "$BFN -> $NFN"
  mv "$FN" "$NFN"
done

Upvotes: 2

Epsilon Prime
Epsilon Prime

Reputation: 4586

I like the PERL cookbook's rename script for this. It may not be /bin/sh but you can do regular expression-like renames.

The /bin/sh method would be to use sed/cut/awk to alter each filename inside a for loop. If the directory is large you'd need to rely on xargs.

Upvotes: 0

Related Questions