如何将文件移动到按日期分隔的子目录中

如何将文件移动到按日期分隔的子目录中

我有一个很大的目录,其中包含过去几年最后修改的文件。有没有一个简单的命令或命令我可以在交互式 bash shell 中输入一次,该命令可以创建子目录,每个子目录的名称都是四位数的年份,并且当我不能依赖其中的任何信息时,将相应的文件移动到每个子目录中关于文件年龄的文件名,最后验证我运行的所有内容是否正常工作并且没有丢失任何文件或数据?

例如,给出这个完全假的例子:

$ cd ~/Documents
$ ls -lhrt
...
-rw-r--r--  4 user   user    4.0K Jun 29  2017 oldfile.txt
-rw-r--r--  4 user   user    4.0K May 15  2018 2018file.md
-rw-r--r--  4 user   user    4.0K Apr 14  2019 04.dat
-rw-r--r--  4 user   user    4.0K Jul 21  2019 somepage.html
drw-r--r--  4 user   user    4.0K Jul 21  2019 somepage_files
-rw-r--r--  4 user   user    4.0K Mar 13  2020 march.dat
-rw-r--r--  4 user   user    4.0K Feb 12 18:03 file02.dat
-rw-r--r--  4 user   user    4.0K Oct 11 18:03 OctReport.txt

当我完成后,我想得到以下结果:

$ cd ~/Documents
$ find .
.
./2017
./2017/oldfile.txt
./2018
./2018/2018file.md
./2019
./2019/04.dat
./2019/somepage.html
./2019/somepage_files
./2019/somepage_files/...
./2020
./2020/march.dat
./2021
./2021/file02.dat
./2021/OctReport.txt

答案1

您可以使用for循环来date获取每个文件的修改年份(这里假设 GNUdate或其选项兼容-r)来创建目录。然后每个文件将被移动到各自的目录。

for file in *; do
    [ ! -L "$file" ] &&
      dir_name=$(date -r "$file" +%Y) &&
      mkdir -p "$dir_name" &&
      mv -- "$file" "$dir_name"
done

答案2

使用珀尔 renamerename实用程序(不要与util-linux 或任何其他实用程序混淆rename):

$ rename -n -e 'BEGIN {use POSIX};
                next unless -f $_;
                my $Y = strftime "%Y", localtime((stat $_)[9]);
                mkdir $Y unless -d $Y;
                s=^=$Y/=' *
rename(04.dat, 2019/04.dat)
rename(2018file.md, 2018/2018file.md)
rename(file02.dat, 2021/file02.dat)
rename(march.dat, 2020/march.dat)
rename(OctReport.txt, 2021/OctReport.txt)
rename(oldfile.txt, 2017/oldfile.txt)
rename(somepage_files, 2019/somepage_files)
rename(somepage.html, 2019/somepage.html)

-n选项使其成为一次试运行。-v当您确定它会执行您想要的操作时,将其删除(或替换为详细输出)。例如

$ find . -type f -ls
   864326      9 drwxr-xr-x   2 cas      cas            10 Dec 22 18:08 .
   863863      1 -rw-r--r--   1 cas      cas             0 Oct 11 18:03 ./OctReport.txt
   863999      1 -rw-r--r--   1 cas      cas             0 Jul 21  2019 ./somepage_files
   864123      1 -rw-r--r--   1 cas      cas             0 Mar 13  2020 ./march.dat
   863997      1 -rw-r--r--   1 cas      cas             0 May 15  2018 ./2018file.md
   864122      1 -rw-r--r--   1 cas      cas             0 Apr 14  2019 ./04.dat
   863862      1 -rw-r--r--   1 cas      cas             0 Feb 12  2021 ./file02.dat
   863998      1 -rw-r--r--   1 cas      cas             0 Jul 21  2019 ./somepage.html
   863996      1 -rw-r--r--   1 cas      cas             0 Jun 29  2017 ./oldfile.txt


$ rename -v -e 'BEGIN {use POSIX}; next unless -f $_; my $Y = strftime "%Y", localtime((stat $_)[9]); mkdir $Y unless -d $Y; s=^=$Y/=' *
04.dat renamed as 2019/04.dat
2018file.md renamed as 2018/2018file.md
file02.dat renamed as 2021/file02.dat
march.dat renamed as 2020/march.dat
OctReport.txt renamed as 2021/OctReport.txt
oldfile.txt renamed as 2017/oldfile.txt
somepage_files renamed as 2019/somepage_files
somepage.html renamed as 2019/somepage.html


$ find . -type f -ls
   863996      1 -rw-r--r--   1 cas      cas             0 Jun 29  2017 ./2017/oldfile.txt
   863862      1 -rw-r--r--   1 cas      cas             0 Feb 12  2021 ./2021/file02.dat
   863863      1 -rw-r--r--   1 cas      cas             0 Oct 11 18:03 ./2021/OctReport.txt
   863999      1 -rw-r--r--   1 cas      cas             0 Jul 21  2019 ./2019/somepage_files
   864122      1 -rw-r--r--   1 cas      cas             0 Apr 14  2019 ./2019/04.dat
   863998      1 -rw-r--r--   1 cas      cas             0 Jul 21  2019 ./2019/somepage.html
   864123      1 -rw-r--r--   1 cas      cas             0 Mar 13  2020 ./2020/march.dat
   863997      1 -rw-r--r--   1 cas      cas             0 May 15  2018 ./2018/2018file.md

有关这些 perl 函数的详细信息,请参阅perldoc -f stat和。perldoc -f localtime详细信息strftime()可以在 中找到perldoc POSIX

顺便说一句,rename可以在命令行上或从标准输入中将文件名作为参数(如果使用重命名选项,甚至可以使用 NUL 分隔的文件名-0,这在处理包含换行符和其他烦人字符的文件名时很有用)。看man rename

答案3

find在这里可能很有用 - 请注意,printf需要 GNU- find

  1. 创建必要的目录

    find -maxdepth 1 -mindepth 1 -type f -printf '%TY\n' | sort -u | xargs mkdir

  2. 相应地移动文件:

    find -maxdepth 1 -mindepth 1 -type f -printf '%f\0%TY/%f\0' | xargs -0 -l2 mv --

在步骤 2 中使用 NUL 分隔的名称使得此方法对于包含空格、换行符等的文件名非常稳健。

当涉及到包含目录时(你的文本说的是文件,但你的示例包含目录),必须在步骤 2 中排除以年份命名的目录:

find -maxdepth 1 -mindepth 1 ! -name '[12][0129][0-9][0-9]' -printf '%f\0%TY/%f\0' | xargs -0 -l2 mv

...假设您没有早于 1000 年或晚于 2999 年的文件。


也许您还想更新目录的年龄?然后运行:

for dir in [12][0129][0-9][0-9] ; do
   touch -d ${dir}-01-01 $dir
done

这样,目录的年龄将设置为相应年份的一月一日,并且将来基于日期的搜索将得到简化。

相关内容