1

I have files that look like this:

ABC_20210101.csv
ABC_20210101_imp.csv

I would like to update the dates in the file name to a specific date:

ABC_20201231.csv
ABC_20201231_imp.csv

How can I go about replacing these file names?

4
  • Is it always ABC_ then eight digits to be replaced, then the trailer? Commented Feb 9, 2021 at 16:03
  • It could be anything really. For instance it could be ABC_DEF_20210101.csv Only thing that is constant is that it will either be yyyymmdd.csv or yyyymmdd_imp.csv Commented Feb 9, 2021 at 16:51
  • So it's anything, then eight digits that need to be replaced, then either _imp.csv or .csv Commented Feb 9, 2021 at 16:59
  • yes that's correct Commented Feb 9, 2021 at 17:03

4 Answers 4

1

With GNU Parallel:

ls | parallel mv {} '{= s/(.*)\d{8}/${1}20210131/ =}'

Tested on:

this_123456789_file_19991231_some.thing

You can include GNU Parallel directly in the script, if you do not have permission to install software on the system you will be running on:

parallel --embed > newscript.sh
0

With bash you can split the filenames apart and reassemble them

#!/bin/bash
new="20201231"

for file in *.csv
do
    # Split the filename into its consituent parts
    if [[ "$a" =~ (.*)[0-9]{8}(_imp)?(\.csv)$ ]]
    then
        # Assemble a new filename
        dest="${BASH_REMATCH[1]}${new}${BASH_REMATCH[2]}${BASH_REMATCH[3]}"
        if [[ ! -f "$dest" ]]
        then
            # No destination so rename
            [[ -t 2 ]] && echo "Rename $file as $dest" >&2
            echo mv -f "$file" "$dest"    # Remove "echo" to action
        fi
    fi
done

This is much easier with rename but not all systems have it installed (remove -n or replace with -v to action):

new="20201231" rename -n 's/(.*)?[0-9]{8}(_imp)?(\.csv)$/$1$ENV{new}$2$3/' *.csv

If you don't have bash or rename then sed can be used to generate the necessary target filename. Arguably this is easier code to read (apart from the RE, which has many backslash escapes to satisfy sed's ERE handling)

new="20201231"

for file in *.csv
do
    # Generate new filename
    dest=$(
        printf "%s\n" "$file" |
        sed -n 's/\(.*\)[0-9]\{8\}\(_imp\)\{0,1\}\(\.csv\)$/\1---\2\3/p'
    )

    if [ -n "$dest" ] && [ ! -f "$dest" ]
    then
        # Pattern substitued and target does not exist so rename
        [[ -t 2 ]] && echo "Rename $file as $dest" >&2
        echo mv -f "$file" "$dest"    # Remove "echo" to action
    fi
done
0

One way to rename the files isvto first select the files using find utility abd then pass them onto sed which will construct the new name and tgen pass a pair to xargs to call mv command to do the renaming.

d0=19710110
d8=$(seq -f '[0-9%g]' 8 | paste -sd'\0')
find . -maxdepth 1 -type f \( -name "*_${d8}_imp.csv" -o -name "*_$d8.csv" \) -print |
sed -Ee "h;/_[0-9]{8}((_imp)?[.]csv)\$/s//_$d0\1/;x;G" |
xargs -r -d'\n' -n2 -t mv -i
0

The standard POSIX sh shell allows us to write patterns like

./*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv

and

./*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_imp.csv

These would match the files you're interested in, in the current directory. (You could make these more specific, obviously, like ./*20[0-2][0-9][01][0-9][0-3][0-9].csv, which would still allow for matching invalid dates.) Then names matching these patterns could then be renamed like so:

for name in ./*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv
do
    [ ! -e "$name" ] && continue
    newname=${name%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].csv}20201231.csv
    mv "$name" "$newname"
done

for name in ./*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_imp.csv
do
    [ ! -e "$name" ] && continue
    newname=${name%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_imp.csv}20201231_imp.csv
    mv "$name" "$newname"
done

The ${variable%pattern} substitution removes the string that matches pattern from the end of the value $variable.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .