With zsh
:
$ autoload -Uz zmv # best in ~/.zshrc
$ LC_ALL=C zmv -f -n '(file)(|_(<->))(.csv)(#qnOn)' '${1}_$(($3+1))$4'
mv -- file_11.csv file_12.csv
mv -- file_10.csv file_11.csv
mv -- file_9.csv file_10.csv
mv -- file_8.csv file_9.csv
mv -- file_7.csv file_8.csv
mv -- file_6.csv file_7.csv
mv -- file_5.csv file_6.csv
mv -- file_4.csv file_5.csv
mv -- file_3.csv file_4.csv
mv -- file_2.csv file_3.csv
mv -- file_1.csv file_2.csv
mv -- file.csv file_1.csv
Or with the 0
-padding using the ${(l[2][0])...}
left-padding parameter expansion flag:
$ LC_ALL=C zmv -fn '(file)(|_(<->))(.csv)(#qnOn)' '${1}_${(l[2][0])$(($3+1))}$4'
mv -- file_11.csv file_12.csv
mv -- file_10.csv file_11.csv
mv -- file_9.csv file_10.csv
mv -- file_8.csv file_09.csv
mv -- file_7.csv file_08.csv
mv -- file_6.csv file_07.csv
mv -- file_5.csv file_06.csv
mv -- file_4.csv file_05.csv
mv -- file_3.csv file_04.csv
mv -- file_2.csv file_03.csv
mv -- file_1.csv file_02.csv
mv -- file.csv file_01.csv
(remove the -n
(dry-run) if OK).
That renames files that are generated by that extendedglob
glob where:
<->
matches any sequence of digits (it's the <x-y>
decimal integer number matching operators with no bound specified)
(#q...)
specifies glob qualifiers where:
n
: toggles numericglobsort
On
: sorts in reverse by name. So file8
comes before file7
but also thanks to numericglobsort above, file10
comes before file9
. The LC_ALL=C
is to make sure the _
and .
are not ignored in the sorting algorithm so file.csv
comes after file_1.csv
In the replacement, $1
, $2
... contain what is matched by each respective pair of (
, )
in the pattern like in perl
's s///
operator.
The -f
is there to disable zmv
's sanity checks which would complain for instance that file6.csv
would be renamed to an already existing file7.csv
(even though by that time, file7.csv
would have already been renamed to file8.csv
).
You could use perl's rename
to do the renaming but you'd still need something like zsh
globs to pass the list in the right order:
$ (LC_ALL=C; rename -f -n '
s{^(file)(?:_(\d*))?(\.csv)\Z}{
"${1}_" . (($2 // 0) + 1) . $3}se
' file(|_<->).csv(nOn))
rename(file_11.csv, file_12.csv)
rename(file_10.csv, file_11.csv)
rename(file_9.csv, file_10.csv)
rename(file_7.csv, file_8.csv)
rename(file_6.csv, file_7.csv)
rename(file_5.csv, file_6.csv)
rename(file_4.csv, file_5.csv)
rename(file_3.csv, file_4.csv)
rename(file_2.csv, file_3.csv)
rename(file_1.csv, file_2.csv)
rename(file.csv, file_1.csv)
Replace (($2 // 0) + 1)
with sprintf("%02d", ($2 // 0) + 1)
for instance to do the 0-padding.
If you don't have and can't install zsh
, but you're on a GNU system, you can get those files in the right order with bash
as:
eval "files=(
$(
shopt -s extglob failglob
export LC_ALL=C
ls --quoting-style=shell-always -rvd file?(_+([0-9])).csv
)
)"
Relying on GNU ls -rv
to list the files in reverse version order.
And then call rename ... "${files[@]}"
with the same rename
code as above to do the renaming.
Here, as the names of the files are very simple and assuming there's no other file that matches file*.csv
, and with the default value of $IFS
, you could also do (still with GNU ls but with any Bourne-like shell):
rename ... $(LC_ALL=C ls -rvd file*.csv)
rename
– Chris Davies Aug 21 '21 at 06:32