如何使用 bash 终端捕获表达式?

如何使用 bash 终端捕获表达式?

我有共享相同命名法的文件log_<date>.txt分散在嵌套目录中root。其中<date>是以 YYYYmmddHHMM 格式创建文件的日期。对于代码兼容性问题,我想将所有文件重命名为<date>_log.txt

我知道我必须捕获该<date>表达式,但如何在 bash 中做到这一点?另外,如何在嵌套目录中执行此操作?

答案1

假设您在 shell 变量中有这些文件之一的名称namelog_然后使用 完成从变量值中删除该位${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选项使bashshell消除不匹配的模式而不是让它们不扩展。这意味着如果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

其他更改包括启用globstarshell 选项,该选项允许我们用于**匹配子目录。另外,我还选择了目录路径,以便能够将其添加到新名称之前。

您还可以用于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'szmvmmv的基于 perl 的变体之一rename

zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF

或者

prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt

相关内容