3

I have a very long list of folders with the following naming convention

ABS1789_2563-01
ABS1789_2563-02
ABS1789_2563-02

.
.
.

How can I add " - " between ABS and 1789 then replace "_" by "-" between 1789 and 2563 using bash?

3 Answers 3

4
IFS="\n"                        # Handle files with spaces in the names
for file in ABS*; do
    newfile="${file/ABS/ABS-}"  # Add the hyphen following ABS
    newfile="${newfile/_/-}"    # Change the underscore to a hyphen
    mv "$file" "$newfile"       # Make the change
done

In light of Tony's comments below, a more general use version could be as follows:

IFS="\n"                          # Handle files with spaces in the names
for file in ABS*; do
    newfile="${file/foo/bar}"     # Replace foo with bar
    newfile="${newfile/baz/quux}" # Replace baz with quux (repeat as needed)
    if [[ "$file" == "$newfile" ]]; then
        echo "Not renaming $file - no change decreed."
    elif [[ -f "$newfile" ]]; then
        echo "Not renaming $file - $newfile already exists."
    else
        mv -- "$file" "$newfile"       # Make the change
    fi
done
1
  • 1
    Good answer. Setting IFS is unnecessary, even if your files have spaces (I have tested this in Bash). In the general case (where, unlike here, you can't guarantee a substitution), it may be worth testing [ "x$file" = "x$newfile" ] || mv -- "$file" "$newfile" to avoid mv complaining about old and new being equal if neither substitution matches (I've also protected against files beginning with -, which we know won't happen in this specific case). Commented Apr 28, 2016 at 8:44
2

A suitable answer depends on how the names might vary. You could transform the names using the shell's built-in parameter substitution if you assume constant field-widths. That's relatively limited in scope.

More interesting would be using character classes in sed:

newname=$(echo "$oldname" | sed -e 's/^\([[:alpha:]]\+\)\([[:digit:]]\+\)_/\1-\2-/')

that is, after a leading alphabetic prefix, add a dash, and then after the digits ending with an underscore, change that to a dash.

Unlike the possible solutions using parameter substitution, this approach allows any (nonzero) length from the alphabetic prefix and digits. So you could have this as input:

ABS1789_2563-01
ABS1789_2563-02
ABS1789_2563-02
ABSOLUTE1789_2563-01
ABSURD1789_2563-02
ABSOLVE1789_2563-02
PREFIX1793939389_2563-02

put that in a script

#!/bin/sh
for oldname in `cat foo4.txt`
do
newname=$(echo "$oldname" | sed -e 's/^\([[:alpha:]]\+\)\([[:digit:]]\+\)_/\1-\2-/')
echo "$oldname ->$newname"
done

giving this output (in a suitable loop):

ABS1789_2563-01 ->ABS-1789-2563-01
ABS1789_2563-02 ->ABS-1789-2563-02
ABS1789_2563-02 ->ABS-1789-2563-02
ABSOLUTE1789_2563-01 ->ABSOLUTE-1789-2563-01
ABSURD1789_2563-02 ->ABSURD-1789-2563-02
ABSOLVE1789_2563-02 ->ABSOLVE-1789-2563-02
PREFIX1793939389_2563-02 ->PREFIX-1793939389-2563-02
1

If all starts with ABS1789_2563-

for f in ABS*; do mv "$f" ABS-1789-2563-${f:13}; done

Here

ABS-1789-2563-

is simply hard coded under the assumption all your folders starts with ABS1789_2563-.

${f:13}

expands to the end of parameter $f starting from offset 13 - often referred to as substring. In bash Substring Expansion.

See 3.5.3 Shell Parameter Expansion, section:

${parameter:offset}
${parameter:offset:length}
2
  • Kindly, what is the meaning of 13 at the end of your command. thanks a lot! Commented Apr 27, 2016 at 23:46
  • 1
    ${var:13} evaluates to "the value of the variable var starting at the thirteenth character". So if var were set to qwertyuiopasdfghjkl, ${var:13} would be equivalent to fghjkl. Commented Apr 27, 2016 at 23:50

You must log in to answer this question.