How to batch rename files using bash

01
2014-07
  • Alex Popov

    I know there are lots of such questions, but I couldn't find one (or a combination of several), which describes the things I want to do. I think I need to use regular expressions, but I am not very good with that. I use zsh. I have a folder with files, which I want to rename:

    • I want the files challenge1.rb, challenge2.rb, challenge3.rb, etc. to be renamed to c1.rb, c2.rb etc. Similarly task1.rb and similar must be renamed to t1.rb etc.

    • sample_spec_c1.rb, sample_spec_c2.rb etc. must be renamed to c1_spec.rb, c2_spec.rb etc.

    So I guess I need some combination of regular expressions and iteration, but I don't know how to write the bash script.

  • Answers
  • pabouk

    Here is a short script which will do what you want. You can call the script with a list of files like: ./scriptname *.rb or with directories (it will recurse them): ./scriptname .

    Do not forget to set the executable bit: chmod a+x scriptname.

    #!/bin/sh
    
    suff=rb     # suffix of files to rename
    
    script="$0" # this script name for recursion
    
    for f in "$@" ; do
        if test -d "$f" ; then
            echo "=== recursing directory $f"
            find "$f" -type f -name "*.$suff" -exec "$script" {} +
        elif test -f "$f" ; then
            d="$(dirname "$f")"
            b="$(basename "$f")"
            r="$(echo "$b" | sed -r "s/^([a-zA-Z])[a-zA-Z]*([0-9]+\.${suff})\$/\1\2/;s/^[a-zA-Z]+_([a-zA-Z]+)_([a-zA-Z]+[0-9]+)(\.${suff})\$/\2_\1\3/")"
            echo "-- renaming $f -> $d/$r"
            mv "$f" "$d/$r"
        fi
    done
    

  • Related Question

    How can I batch rename files in bash?
  • Nathan Long

    I've got some files like this:

    database1-backup-01-01-2011.sql
    database2-backup-01-01-2011.sql
    

    ...etc. I want to rename them to add AM, like this:

    database1-backup-01-01-2011-AM.sql
    database2-backup-01-01-2011-AM.sql
    

    What's the most concise way to do that from the bash shell?


  • Related Answers
  • chrish

    Another option:

    for i in *.sql ; do
        mv -v $i ${i%.sql}-AM.sql
    done
    

    This loops through all the .sql files and renames them to end in -AM.sql instead.

    PROTIP: Use $(command) instead of `command` in your scripts (and command-lines), it makes quoting and escaping less of a nightmare.

  • Majenko

    Try this little script:

    #!/bin/sh
    
    FILES=`ls *.sql`
    for FILE in ${FILES}
    {
        BASE=`basename ${FILE} .sql`
        mv ${FILE} ${BASE}-AM.sql
    }
    

    I just typed that from memory so if it doesn't work 100% don't blame me (i.e., back up your data first ;) )

    How it works:

    Collect all files into a variable (you could put this inside the for instead but I like to keep things easy to read):

    FILES=`ls *.sql`
    

    Loop through each file:

    for FILE in ${FILES} { ... }
    

    Get the filename without .sql:

    BASE=`basename ${FILE} .sql`
    

    Rename the file, adding -AM.sql to the base name:

    mv ${FILE} ${BASE}-AM.sql
    
  • Dennis Williamson

    Using the Perl script version of rename:

    rename 's/\.sql$/-AM$&/' *.sql
    

    Using the util-linux-ng version of rename (but only if ".sql" only appears at the end of the filename):

    rename .sql -AM.sql *.sql
    

    Using mmv:

    mmv '*.sql' '#1-AM.sql'
    
  • Nathan Long

    Since a Perl script has been suggested, here's a Ruby script to do the same:

    `ls *.sql`.split("\n").each do |filename|
      new_filename = filename.split('.').join('-AM.')
      `mv #{filename} #{new_filename}`
    end