Linux

根據文件名中的日期和時間創建文件並將其排列到文件夾中

  • April 11, 2022

我在一個文件夾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 腳本來實現上述文件夾中的文件分組?

使用perl rename實用程序:

注意:perl 重命名也稱為file-renameperl-renameprename。不要與具有完全不同和不兼容的功能和命令行選項的rename實用程序混淆。util-linuxperl rename 是 Debian…IIRC 上的預設重命名,它位於prenameCentos 的軟體包中,並且該命令應該prenamerename.

$ 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當您確定重命名腳本將執行您想要的操作時,將其刪除(或替換為詳細輸出)。

該腳本首先提取每個文件名的日期小時部分(跳過任何不匹配的文件名)。然後它為dateand創建目錄date/hour,然後將文件名重命名為這些目錄。

這假定文件名在目前目錄中。如果不是,您必須調整m//第一行中的匹配正則表達式s===倒數第二行中的替換正則表達式。


使用File::Path 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 模組來重命名和/或移動文件。


第三個版本,這個使用File::Basename將輸入路徑名拆分為$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";

要了解perl rename實用程序如何工作的關鍵是,如果rename 腳本修改了$_變數,那麼 rename 將嘗試將文件重命名為$_. 如果$_未更改,則不會嘗試重命名它。這就是為什麼您可以使用任何perl 程式碼來重命名文件的原因 - 所要做的就是更改$_。大多數情況下,您可能會使用非常簡單sed的類似重命名腳本(例如rename 's/ +/_/g' *,將文件名中的空格重命名為下劃線),但重命名算法可以根據您的需要變得複雜。

$_在 perl 中是一個非常重要的變數 -如果程序員沒有指定一個. 它還用作多個運算符(如m//s///tr///)的預設操作數以及許多(但不是全部)函式的預設參數。查看man perlvar並蒐索$_(您需要在 less as 中將其轉義\$_)。


順便說一句,我之前沒有提到的一件事rename是它可以將文件名作為命令行或標準輸入的參數。它預設為來自 stdin 的換行符分隔輸入(因此它不適用於包含換行符的文件名 - 令人討厭但完全有效的可能性)。您可以使用該-0參數使其使用 NUL 分隔輸入而不是換行符分隔…因此,它可以處理任何文件名,從任何可以生成 NUL 分隔文件名列表的內容中獲取輸入(例如find ... -print0,但它可能更好只使用find’s-exec ... {} +選項)。

rename``-f除非您使用其or--force選項,否則還將拒絕重命名現有文件的文件。

引用自:https://unix.stackexchange.com/questions/698715