I have a set of files in a directory. Some of the filenames contain an eight-digit date, like so:
test_file_20220101.txt
...
test_file_20220110.txt
I want to use a shell script to rename them such that the dates have dashes, like so:
test_file_2022-01-01.txt
...
test_file_2022-01-10.txt
I have some shell code which works (mostly) fine at the command line (once echo
is removed*):
for f in ./*;
do suffix="${f##*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]}";
number="${f%"$suffix"}";
number="${number##*[\!0-9]}";
year="${number:0:4}";
month="${number:4:2}";
day="${number:6:2}";
echo mv "$f" "${f/$number/$year-$month-$day}";
done
However, when I add #!/bin/sh
, #!/bin/bash
, or #!/bin/zsh
at the start, save it as a script (with execution permissions), and run the script, it doesn’t effect the filename change, and I get this in stdout
:
echo mv ./test_file_20220110 ./test_file_20220110
* Remove echo changes nothing; the undesired results are the same with the shell script. I don’t believe echo is the problem.
I’ve also tried, without success: replacing all ”
with ’
or all ’
with ”
and removing all quotation marks. All combinations produce the same erroneous script output. The output of running the script with set -v
added doesn’t help me, but it is:
for f in './*'
suffix=
number=./test_file_20220101
number=
year=
month=
day=
echo mv ./test_file_20220101 ./test_file_20220101
mv ./test_file_20220101 ./test_file_20220101
... [etc.]
Configuration:
- default shell:
zsh
- macOS 12.1 “Monterey”
Separately, and as an additional improvement, I’d like to fix the fact that some files which don’t have the expected eight-digit pattern end up being renamed to something like --other_filename.txt
. I use the following command separately, after using the above command at the CLI, to fix this successfully.
for f in *; do echo mv -- "$f" "${f##--}"; done
It would be nice if this “cleanup” command could be in the same script as above as a last “cleanup” sweep.
CodePudding user response:
The set -v
output may not help you, but it tells me that the script goes sideways when trying to extract the number from the filename. In bash, at least, that's because you have quoted the !
in your bracket expression. That is, [\!0-9]
matches one character from the set '!', '0', '1', ... '9', which is not what you want.
You should observe the same at the command line, and if you don't, then I cannot explain that.
This variation on your script works for me:
#!/bin/bash
for f in ./*; do
suffix=${f##*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]}
number=${f%"$suffix"}
number=${number##*[!0-9]}
year=${number:0:4}
month=${number:4:2}
day=${number:6:2}
echo mv "$f" "${f/${number}/${year}-${month}-${day}}"
done
(Changes other than the correction to the bracket expression are stylistic, not functional.)
CodePudding user response:
This Shellcheck-clean code demonstrates another way to do it:
#! /bin/bash -p
date_rx='([[:digit:]][[:digit:]][[:digit:]][[:digit:]])([[:digit:]][[:digit:]])([[:digit:]][[:digit:]])'
date_file_rx="(.*)${date_rx}(.*)"
for file in *; do
if [[ $file =~ $date_file_rx ]]; then
prefix=${BASH_REMATCH[1]}
year=${BASH_REMATCH[2]}
month=${BASH_REMATCH[3]}
day=${BASH_REMATCH[4]}
suffix=${BASH_REMATCH[5]}
echo mv -n -v -- "$file" "${prefix}${year}-${month}-${day}${suffix}"
fi
done