简体   繁体   中英

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.

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, ie 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.

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

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 abtxt 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.

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

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.

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.

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

#!/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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM