根据文件名中的日期和时间创建文件并将其排列到文件夹中

根据文件名中的日期和时间创建文件并将其排列到文件夹中

我的文件夹中有许多文件Main,其命名如下:

2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz
2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz  2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz
2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz  2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz
2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz  2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz
2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz  2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz
2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz

前 10 个字符显示日期,后跟数字(24 小时格式的时间)。其余的是我们可以忽略的文件详细信息。

我想Main根据文件名中的日期在该文件夹内创建文件夹,然后根据文件名中的小时在日期文件夹内创建另一个文件夹。最终我想将文件从Main文件夹移动到相应的小时文件夹中。

Main -> Date -> hh -> file.csv.gz

例如:文件夹2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz中的文件Main最终将位于这样的文件夹中,路径如下Main/2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz

您能否帮助使用 bash 脚本来实现如上所述的文件夹中的文件分组?

答案1

使用该perl rename实用程序:

注意:perl rename 也称为file-renameperl-rename、 或prename。不要与具有完全不同且不兼容的功能和命令行选项rename的实用程序混淆。 util-linuxperl rename 是 Debian...IIRC 上的默认重命名,它位于prenameCentos 上的软件包中,并且该命令应作为prename而不是rename.

$ rename -n 'if (m/(^\d{4}_\d\d_\d\d)_(\d\d)/) {
               my ($date,$hour) = ($1,$2);
               my $dir = "./$date/$hour/";
               mkdir $date;
               mkdir $dir;
               s=^=$dir=
             }' *
rename(2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz, ./2021_10_15/23/2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz)
rename(2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz, ./2021_11_24/21/2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz)
rename(2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz, ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz)
rename(2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz, ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz)
rename(2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz, ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz)
rename(2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz, ./2021_11_24/21/2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz)
rename(2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz, ./2021_11_25/20/2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz)
rename(2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz, ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz)
rename(2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz, ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz)
rename(2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz, ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz)

-n是一个试运行选项,它只会显示它会做什么,而不会实际执行。-v当您确定重命名脚本将执行您想要的操作时,将其删除(或替换为详细输出)。

该脚本的工作原理是首先提取日期小时每个文件名的部分(跳过任何不匹配的文件名)。然后它创建date和的目录date/hour,然后将文件名重命名到这些目录中。

这假设文件名位于当前目录中。如果不是,您必须调整m//第一行中的匹配正则表达式s===倒数第二行中的替换正则表达式。


使用的替代版本文件路径perl 核心模块(包含在 perl 中),而不是使用mkdir两次(该make_path函数的工作方式类似于mkdir -pshell 命令):

$ rename -v 'BEGIN {use File::Path qw(make_path)};
             if (m/(^\d{4}_\d\d_\d\d)_(\d\d)/) {
               my $dir = "./$1/$2/";
               make_path $dir;
               s=^=$dir=
             }' *
2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz renamed as ./2021_10_15/23/2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz
2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz renamed as ./2021_11_24/21/2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz
2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz renamed as ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz
2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz renamed as ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz
2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz renamed as ./2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz
2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz renamed as ./2021_11_24/21/2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz
2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz renamed as ./2021_11_25/20/2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz
2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz renamed as ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz
2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz renamed as ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz
2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz renamed as ./2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz

这实际上并不比第一个版本好,但它确实表明您可以使用任何 Perl 代码、任何 Perl 模块来重命名和/或移动文件。


第三个版本,这个使用文件::基本名称将输入路径名拆分为$path$file部分。它可以处理当前目录或任何其他目录中的文件名。 File::Basename是一个核心 Perl 模块,因此包含在 Perl 中。它提供了三个有用的函数basename()dirname()(其工作方式与同名的 shell 工具类似),fileparse()这就是我在此脚本中使用的函数,将基本名称和目录提取到单独的变量中。

rename -n 'BEGIN {use File::Path qw(make_path); use File::Basename};
           my ($file, $path) = fileparse($_);
           if ($file =~ m/(\d{4}_\d\d_\d\d)_(\d\d)/) {
             my $dir = "$path/$1/$2";
             make_path $dir;
             $_ = "$dir/$file"
           }' /home/cas/rename-test/*
rename(/home/cas/rename-test/2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz, /home/cas/rename-test/2021_10_15/23/2021_10_15_23_35_SIP_CDR_pid3894_ins2_thread_1_4718.csv.gz)
rename(/home/cas/rename-test/2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz, /home/cas/rename-test/2021_11_24/21/2021_11_24_21_15_Gi_pid25961_ins2_thread_1_6438.csv.gz)
rename(/home/cas/rename-test/2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz, /home/cas/rename-test/2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins2_thread_1_6485.csv.gz)
rename(/home/cas/rename-test/2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz, /home/cas/rename-test/2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins3_thread_2_6485.csv.gz)
rename(/home/cas/rename-test/2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz, /home/cas/rename-test/2021_11_24/21/2021_11_24_21_15_Gi_pid27095_ins4_thread_3_6485.csv.gz)
rename(/home/cas/rename-test/2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz, /home/cas/rename-test/2021_11_24/21/2021_11_24_21_15_Gi_pid681_ins5_thread_4_6457.csv.gz)
rename(/home/cas/rename-test/2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz, /home/cas/rename-test/2021_11_25/20/2021_11_25_20_55_Gi_pid29741_ins5_thread_4_7540.csv.gz)
rename(/home/cas/rename-test/2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz, /home/cas/rename-test/2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins3_thread_2_7489.csv.gz)
rename(/home/cas/rename-test/2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz, /home/cas/rename-test/2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins4_thread_3_7488.csv.gz)
rename(/home/cas/rename-test/2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz, /home/cas/rename-test/2021_11_25/20/2021_11_25_20_55_Gi_pid30842_ins5_thread_4_7489.csv.gz)

顺便说一句,修改它以便将文件移动到完全不同的路径是微不足道的 - 只需让它做类似的事情my $dir = "/my/new/path/$1/$2";而不是my $dir = "$path/$1/$2";

关键是要了解如何珀尔 rename实用程序的工作原理是当且仅当重命名脚本修改$_变量,然后重命名将尝试将文件重命名为 的新值$_。如果$_未更改,则不会尝试重命名。这就是为什么你可以使用任何重命名文件的 perl 代码 - 所要做的就是更改$_.大多数情况下,您可能会使用非常简单的sed重命名脚本(例如,rename 's/ +/_/g' *将文件名中的空格重命名为下划线),但重命名算法可以根据您的需要而复杂。

$_是 perl 中非常重要的变量 - 它用作默认变量来保存来自文件句柄和循环迭代器的输入如果程序员没有指定。它还用作多个运算符(如m//, s///, tr///)的默认操作数以及许多(但不是全部)功能。查看man perlvar并搜索$_(您需要在 less as 中转义\$_)。


顺便说一句,我之前没有提到的一件事rename是它可以将文件名作为命令行上的参数或来自标准输入的参数。它默认使用来自标准输入的换行符分隔输入(因此它不适用于包含换行符的文件名 - 这是一个令人讨厌但完全有效的可能性)。您可以使用-0参数使其使用 NUL 分隔的输入而不是换行符分隔...因此,它可以使用任何文件名,从任何可以生成 NUL 分隔的文件名列表的输入中获取输入(例如find ... -print0,但它可能更好仅使用 的find选项-exec ... {} +)。

rename也将拒绝在现有文件上重命名文件,除非您使用其-f--force选项。

答案2

在目录中使用,zsh代替:bashMain

zmodload zsh/files # to get builtin mkdir/mv to speed things up

mkdmv() { mkdir -p -- $2:h && mv -- "$@"; }
zmv -n -P mkdmv '(<->_<->_<->)_(<->)_*.csv' '$1/$2/$f'

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

zmv将在执行任何操作之前运行健全性检查,以帮助避免在发生冲突时丢失数据,这是它相对于大多数其他批量重命名实用程序的优势之一。

<->匹配任何 ASCII 数字序列。如果您希望匹配更具体,您可以(<1970-2099>_<1-12>_<1-31>)_(<0-23>)_*.csv这样做。

相关内容