EpiMan EpiMan - 24 days ago 10
Bash Question

change file names in a directory with a certain pattern at the beginning

I want to remove the numbers from the file names in one directory:

file names:

89_Ajohn_text_phones
3_jpegs_directory
..


What I would like to have

Ajohn_text_phones
jpegs_directory


I tried:

rename 's/^\([0-9]|[0-9][0-9]\)_//g' *


but I did not work.

Answer Source

There are two rename tools. The one you appear to try to use is based on Perl, and as such uses perl-style regular expressions. The escaping rules are a little different from sed; in particular, parentheses for grouping aren't escaped (escaped parentheses are literal parentheses). Use

rename 's/^([0-9]|[0-9][0-9])_//' *

or, somewhat more concisely,

rename 's/^[0-9]{1,2}_//' *

This rename is the default on Debian-based distributions such as Ubuntu.

The other rename tool is from util-linux, and it is a very simple tool that does not work with regexes and cannot handle this particular requirement. It is the default on, for example, Fedora-based distributions, and what's worse, those often don't even package the Perl rename. You can find the Perl rename here on CPAN and put it in /usr/local/bin if you want, but otherwise, your simplest option is probably a shell loop with mv and sed:

for f in *; do
    # set dest to the target file name
    # Note: using sed's extended regex syntax (-r) because it is nice.
    #       Much less escaping of metacharacters is needed. Note that
    #       sed on FreeBSD and Mac OS X uses -E instead of -r, so there
    #       you'd have to change that or use regular syntax, in which
    #       the regex would have to be written as ^[0-9]\{1,2\}_
    dest="$(echo "$f" | sed -r 's/^[0-9]{1,2}_//')"
    if [ "$f" = "$dest" ]; then
        # $f did not match pattern; do nothing
        true;
    elif [ -e "$dest" ]; then
        # avoid losing files.
        echo "$dest already exists!"
    else
        mv "$f" "$dest"
    fi
done

You could put this into a shell script, say rename.sh:

#!/bin/sh

transform="$1"
shift

for f in "$@"; do
    dest="$(echo "$f" | sed -r "$transform")"
    if [ "$f" = "$dest" ]; then
        ## do nothing
        true;
    elif [ -e "$dest" ]; then
        echo "$dest already exists!"
    else
        mv "$f" "$dest"
    fi
done

and call rename.sh 's/^[0-9]{1,2}_//' *.

One caveat remains: in

dest="$(echo "$f" | sed -r "$transform")"

there is a possibility that "$f" could be something that echo considers a command line option, such as -n or -e. I do not know a way to solve this portably (if you read this and know one, please leave a comment), but if you are in a position where tethering yourself to bash is not a problem, you can use bash's here strings instead:

dest="$(sed -r "$transform" <<< "$f")"

(and replace the #!/bin/sh shebang with #!/bin/bash). Such files are rare, though, so as timebombs go, this is not unlikely to be a dud.