将目录名称末尾的年份移到前面,可能会转换为 YYYY-MM-DD 格式

将目录名称末尾的年份移到前面,可能会转换为 YYYY-MM-DD 格式

我将所有图片从 Mac 导出到我的 PC(Windows 11,但也通过 PowerShell 运行 Ubuntu)。导出以如下格式创建了每个目录(数千个文件夹),并删除了原始创建日期:

April 1, 2004         
Amsterdam, September 18, 2018    
Anderson Arbor, March 24, 2011   

我希望能够按日期排序。关于如何将这些格式更改为以下格式的任何想法:

2004, April 1   
2018, Amsterdam, September 18    
2011, Anderson Arbor, March 24

或者更好的是:

2004-04-01    
2018-09-18 Amsterdam     
2011-03-24 Anderson Arbor

答案1

zsh

$ autoload -Uz zmv
$ zmodload zsh/datetime
$ zmv -n '(|(*), )([^ ]## <1-31>, <->)' \
         '$(strftime %F $(strftime -r "%B %d, %Y" $3))${2:+ $2}'
mv -- 'Amsterdam, September 18, 2018' '2018-09-18 Amsterdam'
mv -- 'Anderson Arbor, March 24, 2011' '2011-03-24 Anderson Arbor'
mv -- 'April 1, 2004' 2004-04-01

-n如果满意,请删除(试运行))。

zmv是一个可自动加载的 zsh 函数,用于批量重命名。

它需要两个参数:

  1. 全局模式(必须用引号引起来,因为它的扩展将由其自身完成zmv)。
  2. 将评估一个字符串以弥补每个匹配文件的替换。

在替换中,每对匹配的内容(...)可在$1, $2...

这里,全局模式是(|(*), )([^ ]## <1-31>, <->),其中:

  • (x|y)匹配 或xy因此(|(*), )匹配任何内容或任何内容(*这将最终出现$2在替换中,因为它位于第二对 中(...))以 结尾", "
  • [^ ]匹配除空格之外的任何单个字符,x##匹配一个或多个xs,因此[^ ]##匹配一个或多个非空格字符的任何序列。
  • <->,无<x-y>边界匹配任何数字、任何十进制数字序列。

在替换中,我们使用模块strftime中的内置函数zsh/datetime来解析(使用-r)并重新格式化在 中捕获的日期$3。该内置函数提供了一个简单的标准接口,strftime()并分别提供了ormat 和ass 的strptime()功能。fptimestr

%F是将%Y-%m-%d日期格式化为 YYYY-MM-DD 的缩写,%B表示完整的月份名称、%d月份中的某一天、%Y年份。


我们可以从 langinfo 获取月份名称列表,然后手动转换为月份编号和格式,而不是在命令替换中使用strftime,这意味着每次都分叉一个进程以便能够获取其输出:

zmodload zsh/langinfo
autoload -Uz zmv
months=( ${(v)langinfo[(I)MON_<1-12>]} )
#       1 2     3                 4         5
zmv -n "(|(*), )(${(j[|])months}) (<1-31>), (<->)" \
       '$5-${(l[2][0])months[(I)$3]}-${(l[2][0])4}${2:+ $2}'

在哪里:

  • ${(v)langinfo[(I)MON_<1-12>]}展开为键匹配 的关联数组v的值。$langinfoMON_<1-12>
  • ${(j[|])months} joins 数组的元素|
  • $array[(I)$3]返回与 中存储的模式匹配的数组元素的最大索引$3
  • ${(l[2][0])string} l2eft用s 将字符串填充到长度0

答案2

使用findperlrename实用程序(又名prename, file-renameperl-rename取决于您使用的发行版),加上日期::解析日期格式库模块。

这些模块不包含在 perl 中,它们需要单独安装,或者通过命令行工具或发行版包。他们都在时间日期在 Debian 上,它们可以通过 .bundle 安装sudo apt-get install libtimedate-perl。我不知道该软件包在其他发行版上的名称是什么,但可能非常相似。

顺便说一句,不要将 perl实用程序与具有完全不同且不兼容的功能和命令行选项的实用程序rename混淆。renameutil-linux

反正:

$ mkdir 'April 1, 2004' 'Amsterdam, September 18, 2018' 'Anderson Arbor, March 24, 2011'

$ find . -type d -print0 |
  rename -n -0 'BEGIN {
                  use Date::Parse;
                  use Date::Format;
                  our $date_pattern = qr/[[:alpha:]]+ \d+, \d\d\d\d/;
                };
                our $date_pattern;

                if (/^(.*\/)(.*)\b($date_pattern)$/) {
                  my ($p1, $p2, $d) = ($1, $2, $3);
                  my $t = str2time($d);

                  s/\b$date_pattern//;
                  $_ = sprintf("%s%s%s", $p1, time2str("%Y-%m-%d ",$t), $p2);
                  s/[, ]*$//;
                }'
rename(./April 1, 2004, ./2004-04-01)
rename(./Amsterdam, September 18, 2018, ./2018-09-18 Amsterdam)
rename(./Anderson Arbor, March 24, 2011, ./2011-03-24 Anderson Arbor)

注意:由于该-n选项,这是一次试运行。一旦您确定它执行了您想要的操作,请删除-n或将其替换-v为详细输出。

加载 Date::Parse 和 Date::Format 模块并$date_pattern在块中定义变量 ( )后BEGIN {},此重命名脚本将迭代每个目录名称并尝试匹配“月日,年”模式。

如果成功,则路径名的最后一个元素之外的所有元素都将被捕获/提取到变量中$p1,最终目录元素在日期模式之前的任何“前缀”部分进入变量中$p2(这是可选的,如果丢失,将是一个空字符串),并将日期模式放入变量中$d

然后,匹配的日期$d将转换为一个time_t值(自纪元以来的秒数,1970 年 1 月 1 日午夜)并$t使用该str2time()函数分配给变量。

然后:

  1. 日期模式从目录名称中删除

  2. sprintf和函数time2str()用于通过更改 . 来修改整个文件名以符合新模式$_

    这个简单的作业$_强调了关于 perl 如何工作的一个非常简单但重要的事实rename

    在 perl 中,如果没有指定其他变量名(请参阅并搜索)(包括运算符),则该变量$_用作循环中的默认变量以及许多函数和运算符的默认参数/操作数。man perlvar$_s///

    对于 perl rename,$_用于当前路径/文件名,任何更改都$_将导致其被重命名。如果$_更改,则会更名。如果不改变的话就不会了。

    注意rename 惯于重命名文件或目录以覆盖现有文件名,除非您使用该-f选项强制执行此操作。

  3. 然后从最终目录名称中删除所有尾随空格和逗号(如果有)。


其他注意事项:

  1. $date_pattern变量在块中定义BEGIN{},以便仅在脚本启动时执行一次(而不是对 stdin 上的每个目录名执行一次)。它被定义为变量,因为它被使用了两次 - 如果需要更改它,最好只需要在一处更改它。它被定义为“一个或多个字母字符、一个空格、一个或多个数字、另一个空格和四位数字”。这与您的示例目录名称相匹配,但如果可能存在其他变体,则可能需要进行调整。

    它是用 声明的our,以便它在重命名脚本的整个范围内有效(否则它只能在 BEGIN 块内可用)。 perl 重命名脚本在 perlstrict模式下运行(perldoc strict详细信息请参见)。

    qr是一个 Perl 引用运算符,它引用(并可能预编译,以提高性能)正则表达式 - 性能在这里可能不是问题,我在这里使用它主要是为了解决使用单引号很痛苦的事实在单引号单行脚本内。perldoc -f qr详情请参阅。

  2. 通过使用find ... -print0andrename-0选项,NUL 用作文件名分隔符,因此这适用于任何目录名,甚至包含换行符和其他空格或 shell 元字符(例如;,等)的目录名&。NUL>仅有的不能出现在路径名中的字符。

答案3

使用珀尔的rename:

Time::Piece位于 Perl 的核心中,被认为是比“魔法”更干净的方式,而不是 100% 可靠的Date::Parse模块。

rename -n '
    BEGIN{use Time::Piece}
    my $d = localtime;
    my $months_re = join "|", map { "$_\\w+" } $d->mon_list;
    s/
        ($months_re)
        \s+(\d+),\s+(\d{4})
    /
        my $d = Time::Piece->strptime("$1 $2, $3", "%B %d, %Y");
        $d->strftime("%F")
    /xe;
    s/(.*),\s+(\d{4}-\d{2}-\d{2})/$2 $1/;
' ./*/

当输出看起来令人满意时删除-n(又名)。dry-run

输出

rename(Amsterdam, September 18, 2018/, 2018-09-18 Amsterdam/)
rename(Anderson Arbor, March 24, 2011/, 2011-03-24 Anderson Arbor/)
rename(April 1, 2004/, 2004-04-01/)

相关内容