我有一个很大的目录,其中包含过去几年最后修改的文件。有没有一个简单的命令或命令我可以在交互式 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
使用珀尔 rename
rename
实用程序(不要与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
:
创建必要的目录
find -maxdepth 1 -mindepth 1 -type f -printf '%TY\n' | sort -u | xargs mkdir
相应地移动文件:
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
这样,目录的年龄将设置为相应年份的一月一日,并且将来基于日期的搜索将得到简化。