Home > other >  How to fix shell script that works at the command line? Script uses `mv`, quotation marks, parameter
How to fix shell script that works at the command line? Script uses `mv`, quotation marks, parameter

Time:05-27

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
  • Related