如何修复在命令行运行的 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.]
配置:
- 默认 shell:
zsh
- macOS 12.1“蒙特雷”
另外,作为一项额外的改进,我想修复这样一个事实,即一些没有预期的八位数模式的文件最终被重命名为 --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
我在一个目录中有一组文件。一些文件名包含一个八位数的日期,例如:
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.]
配置:
- 默认 shell:
zsh
- macOS 12.1“蒙特雷”
另外,作为一项额外的改进,我想修复这样一个事实,即一些没有预期的八位数模式的文件最终被重命名为 --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