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?
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
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
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
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
.
ABC_
then eight digits to be replaced, then the trailer?_imp.csv
or.csv