我有共享相同命名法的文件log_<date>.txt
分散在嵌套目录中root
。其中<date>
是以 YYYYmmddHHMM 格式创建文件的日期。对于代码兼容性问题,我想将所有文件重命名为<date>_log.txt
我知道我必须捕获该<date>
表达式,但如何在 bash 中做到这一点?另外,如何在嵌套目录中执行此操作?
答案1
假设您在 shell 变量中有这些文件之一的名称name
。log_
然后使用 完成从变量值中删除该位${name#log_}
,并.txt
使用 完成删除该位${name%.txt}
。剩下的就是日期,而它的格式本质上并不有趣。可以很容易地获取剥离的值并将其添加_log_.txt
到其末尾以创建新名称。
要对单个目录中的文件执行此操作,请使用循环。请注意,我们现在还必须删除变量值开头的目录名称。
shopt -s nullglob
for name in root/log_*.txt; do
newname=root/${name#root/log_} # remove prefix, add root/ back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf 'Would move "%s" to "%s"\n' "$name" "$newname"
# mv -i "$name" "$newname"
done
mv
为了安全起见,我已经注释掉了实际的命令。您应该测试运行一次,看看首先会发生什么。
shellnullglob
选项使bash
shell消除不匹配的模式而不是让它们不扩展。这意味着如果log_*.txt
给定目录中没有文件,则循环根本不会运行。
这可以递归地应用于当前目录(包括当前目录)下的任何此类文件或多或少只需修改我们循环的模式:
shopt -s globstar nullglob
for name in ./**/log_*.txt; do
dirpath=${name%/*} # get path of directory
newname=$dirpath/${name#$dirpath/log_} # remove prefix, add directory path back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf 'Would move "%s" to "%s"\n' "$name" "$newname"
# mv -i "$name" "$newname"
done
其他更改包括启用globstar
shell 选项,该选项允许我们用于**
匹配子目录。另外,我还选择了目录路径,以便能够将其添加到新名称之前。
您还可以用于find
生成循环的输入:
find . -type f -name 'log_*.txt' -exec sh -c '
for name do
dirpath=${name%/*} # get path of directory
newname=$dirpath/${name#$dirpath/log_} # remove prefix, add directory path back
newname=${newname%.txt}_log.txt # remove suffix, add _log.txt back
printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
# mv -i "$name" "$newname"
done' sh {} +
这将找到当前目录或以下目录中名称匹配的所有常规文件log_*.txt
,然后将它们批量传递到内联sh -c
脚本,该脚本基本上执行与上面第二个变体中的循环相同的工作。
答案2
最好是使用批处理文件重命名工具,例如zsh
'szmv
或mmv
的基于 perl 的变体之一rename
:
zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF
或者
prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt