如何修复在命令行运行的 shell 脚本?脚本使用 `mv`、引号、参数扩展和变量

How to fix shell script that works at the command line? Script uses `mv`, quotation marks, parameter expansion, and variables

我在一个目录中有一组文件。一些文件名包含一个八位数的日期,例如:

test_file_20220101.txt
...
test_file_20220110.txt

我想使用 shell 脚本重命名它们,使日期带有破折号,如下所示:

test_file_2022-01-01.txt
...
test_file_2022-01-10.txt

我有一些 shell 代码可以在命令行(大部分)正常工作(一旦 echo 被删除*):

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

但是,当我在开头添加#!/bin/sh#!/bin/bash#!/bin/zsh时,将其保存为脚本(具有执行权限),而运行脚本,它不会影响文件名更改,我在 stdout:

中得到了这个

echo mv ./test_file_20220110 ./test_file_20220110

* 删除 echo 没有任何改变; shell 脚本的不良结果相同。我不认为回声是问题所在。

我也尝试过,但没有成功:将所有 替换为 或将所有 替换为 并删除所有引号。所有组合都会产生相同的错误脚本输出。添加了 set -v 的 运行ning 脚本的输出对我没有帮助,但它是:

+ 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.]

配置:


另外,作为一项额外的改进,我想修复这样一个事实,即一些没有预期的八位数模式的文件最终被重命名为 --other_filename.txt 之类的东西。我单独使用以下命令,在 CLI 中使用上述命令后,成功修复此问题。

for f in *; do echo mv -- "$f" "${f##--}"; done

如果此“清理”命令与上次“清理”扫描位于与上面相同的脚本中,那就太好了。

set -v 的输出可能对您没有帮助,但它告诉 当试图从文件名中提取数字时,脚本跑偏了。至少在 bash 中,那是因为您在方括号表达式中引用了 !。也就是说,[\!0-9] 匹配集合 '!', '0', '1', ... '9' 中的一个字符,这不是您想要的。

您应该在命令行中观察到同样的情况,如果您不这样做,那么我无法解释。

你脚本的这个变体对我有用:

#!/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

(除了更正括号表达式之外的更改是风格上的,而不是功能上的。)

这段 Shellcheck-clean 代码演示了另一种方法:

#! /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