【问题标题】:How to use bash string formatting to reverse date format?如何使用 bash 字符串格式化来反转日期格式?
【发布时间】:2018-05-10 08:57:19
【问题描述】:

我有很多文件被命名为:MM-DD-YYYY.pdf。我想将它们重命名为 YYYY-MM-DD.pdf 我确信有一些 bash 魔法可以做到这一点。这是什么?

【问题讨论】:

  • 你自己尝试了什么?我们可以协助您完成工作,而不是提供完整的解决方案
  • @jlconlin:祝您飞行安全,抵达后查看我们的答案! ;-)

标签: bash date-manipulation


【解决方案1】:

对于当前目录下的文件:

for name in ./??-??-????.pdf; do
    if [[ "$name" =~ (.*)/([0-9]{2})-([0-9]{2})-([0-9]{4})\.pdf ]]; then
        echo mv "$name" "${BASH_REMATCH[1]}/${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}.pdf"
    fi
done

递归,在当前目录中或下:

find . -type f -name '??-??-????.pdf' -exec bash -c '
    for name do
        if [[ "$name" =~ (.*)/([0-9]{2})-([0-9]{2})-([0-9]{4})\.pdf ]]; then
            echo mv "$name" "${BASH_REMATCH[1]}/${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}.pdf"
        fi
    done' bash {} +

bash 中启用globstar shell 选项可以让我们执行以下操作(也将像上面的解决方案一样处理当前目录中或当前目录下的所有文件):

shopt -s globstar

for name in **/??-??-????.pdf; do
    if [[ "$name" =~ (.*)/([0-9]{2})-([0-9]{2})-([0-9]{4})\.pdf ]]; then
        echo mv "$name" "${BASH_REMATCH[1]}/${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}.pdf"
    fi
done

这三个解决方案都使用正则表达式来挑选文件名的相关部分,然后将这些部分重新排列为新名称。它们之间的唯一区别是路径名列表的生成方式。

为了安全起见,代码前缀 mvecho。要真正重命名文件,请删除 echo(但至少运行一次 with echo 以查看它是否符合您的要求)。

【讨论】:

  • @Inian 谢谢! glob 仅用于选择文件名。在 find 变体中,您甚至可以完全删除 -name 位,而在第一个变体中,您还不如在 *.pdf 上进行 glob。我这样做是为了证明这些模式是相关的。至于效率,我非常怀疑它会产生明显的不同。
  • for name in **/??-??-????.pdf 在这种情况下会比使用 find 更简单,假设 OP 没有被 bash 3.x 卡住。
  • @chepner 启用 extglob,是的。我稍后可能会提到这一点(现在在电话上)。
  • 这太棒了! (抱歉回复慢。)不幸的是,我不能使用globstar。显然我的 bash 在我的 Mac 上太旧了。那么BASH_REMATCH 知道从哪里获取正则表达式组?
【解决方案2】:

从命令行直接的办法例如:

$ ls
10-01-2018.pdf  11-01-2018.pdf  12-01-2018.pdf
$ ls [0-9]*-[0-9]*-[0-9]*.pdf|sed -r 'p;s/([0-9]{2})-([0-9]{2})-([0-9]{4})/\3-\1-\2/'|xargs -n2 mv
$ ls
2018-10-01.pdf  2018-11-01.pdf  2018-12-01.pdf

ls 输出通过管道传输到 sed,然后我们使用 p 标志打印参数而不进行修改,即文件的原始名称,以及 小号以执行和输出转换。

ls + sed 结果是由 old_file_namenew_file_name 序列组成的组合输出。

最后,我们通过xargs 将生成的提要通过管道传递,以获得文件的有效重命名。

xargs人强>:

-n number 使用尽可能多的标准输入参数执行命令,最多使用 number 个参数。

【讨论】:

    【解决方案3】:

    您可以使用非常接近klashxx 之一的以下命令:

    for f in *.pdf; do echo "$f"; mv "$f" "$(echo "$f" | sed 's@\(..\)-\(..\)-\(....\)@\3-\2-\1@')"; done
    

    之前:

    ls *.pdf
    12-01-1998.pdf  12-03-2018.pdf
    

    之后:

    ls *.pdf
    1998-01-12.pdf  2018-03-12.pdf
    

    此外,如果您的文件夹中有其他不遵守此格式的 pdf 文件,您可以做的是仅选择遵守该格式的文件:MM-DD-YYYY.pdf 这样做使用以下命令:

    for f in `find . -maxdepth 1 -type f -regextype sed -regex './[0-9]\{2\}-[0-9]\{2\}-[0-9]\{4\}.pdf' | xargs -n1 basename`; do echo "$f"; mv "$f" "$(echo "$f" | sed 's@\(..\)-\(..\)-\(....\)@\3-\2-\1@')"; done
    

    解释:

    • find . -maxdepth 1 -type f -regextype sed -regex './[0-9]\{2\}-[0-9]\{2\}-[0-9]\{4\}.pdf 此查找命令将仅查找当前工作目录中符合您的语法并提取其基本名称的文件(删除开头的 ./、文件夹和其他类型的文件)不考虑同名,其他*.pdf文件也会被忽略。

    • for each file 你做了一个移动,然后使用 sed 计算生成的文件名,并反向引用 MM、DD 和 YYYY 的 3 个组

    【讨论】:

      【解决方案4】:

      对于这些简单的文件名,使用更详细的模式,您可以稍微简化循环体:

      twodigit=[[:digit:]][[:digit:]]
      fourdigit="$twodigit$twodigit"
      for f in $twodigit-$twodigit-$fourdigit.pdf; do
        IFS=- read month day year <<< "${f%.pdf}"
        mv "$f" "$year-$month-$day.pdf"
      done
      

      这基本上是@Kusalananda's answer,但没有正则表达式匹配的冗长。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-29
        • 1970-01-01
        相关资源
        最近更新 更多