使用
find $dirname -printf "%T@ %Tc %p\n" | sort -n | tail -n 15
我可以在下面的目录结构中找到第 n 个(在我的示例中 n = 15)最新文件$dirname
。现在我想将文件复制(或链接)到一个目录中$current
(我想定期将其作为后台脚本运行以获取所有新添加的文件的视图。
这样做的背景是,我将目录结构(通过rclone
)从 ms sharepoint 镜像到我的笔记本电脑,然后我想要一个目录$current
显示所有 n 个最新修改的文件。
答案1
尝试类似以下 bash 脚本,从以下位置运行cron
:
#!/bin/bash
# exit on any error
set -e
set -o pipefail
# defaults for the options
n=15
dirname='/default/path/to/your/files'
current='/default/path/to/the/current/dir'
while getopts ':n:d:c:' opt ; do
case "$opt" in
n) n="$OPTARG";;
d) dirname="$OPTARG";;
c) current="$OPTARG";;
:) echo "Error: '-$OPTARG' requires an argument" >&2 ; exit 1 ;;
*) echo "Error: unknown option '-$OPTARG'" >&2 ; exit 1 ;;
esac
done
if [ ! -d "$current" ] ; then
if ! mkdir -p "$current" ; then
echo "Error: '$current' can't be created or already exists and isn't a directory" >&2
exit 1
fi
fi
rm -f "$current"/*
# alternatively, to delete only symlinks in $current:
# find -H "$current/" -maxdepth 1 -type l -delete
find "$dirname/" -type f -printf "%T@\t%p\0" |
sort -z -r -n |
head -z -n "$n" |
sed -z -e 's/^[^\t]*\t//' |
xargs -0r readlink -e -z |
xargs -0r ln -s -t "$current/"
这使用 NUL 作为文件名之间的分隔符,因此它将适用于包含任何有效字符的文件名(NUL 是仅有的在路径/文件名中无效的字符)。
管道中的脚本sed
删除时间戳字段(直到并包括第一的TAB 字符 - 必需的,因为 TAB 是文件名中的有效字符)。sed
您可以在此处使用,而不是cut -z -f2-
。
我使用了sort -r -n
andhead -n
而不是,tail -n
因为它会稍微快一些 -head
可以在输出前 15 个文件名后立即退出,而tail
必须读取整个输入。
xargs -0r
使用两次。首先用于readlink
获取每个文件名的完整规范路径,然后$current
使用ln
.
find
注意:这需要支持的版本,以及、、和printf
的版本sort
head
readlink
sed
支持,以及支持目标目录选项-z
的版本,以及支持的版本。例如所有这些的 GNU 版本。ln
-t
xargs
-0
另外,我不记得\t
for tab 在其他版本的 sed 中是否可以在括号表达式内工作,但它肯定可以在 GNU sed 中工作。
如果你运行的是 Linux,那么你所拥有的就是 GNU 版本的 coreutils、findutils、sed 等(除非你做了一些非常奇怪和莫名其妙的疯狂的事情,比如安装非 GNU 版本)。
答案2
在 zsh 中,许多复杂的find
调用会转化为一些神秘但简短的内容。与其他方式相比,zsh 中的自定义排序特别方便。以下 zsh 命令列出了以下 15 个最新的常规文件$dirname
:
print -rC1 -- $dirname/**/*(.Nom[1,15])
**
做递归通配符。最后括号里的字符是全局限定符:.
选择常规文件,N
防止没有匹配文件时出错,om
按修改时间排序(最新的在前),并[1,15]
选择前 15 个文件。
由于这是对命令行的通配符扩展,因此您不必担心文件名中的 shell 特殊字符。如果$dirname
以 开头,您确实必须担心前导破折号会被解释为选项-
。
例如,下面是简单的(未经测试的)代码,用于更新recent
包含指向everything
.当存在名称冲突时,代码会向文件名添加后缀。
#!/usr/bin/env zsh
set -e
recent=(/everything/**/*(.Nom[1,15]))
rm -f /recent/*(@N)
for file in $recent; do
target=/recent/$file:t
i=1
while [[ -e $target ]]; do
target=$file:t:r-$i
if [[ -n $file:e ]]; then target+=.$file:e; fi
((++i))
done
ln -s -- $file $target
done
后缀:t
, :r
,:e
是历史扩展修改器分别从文件名中提取基本名称(尾部)、不带扩展名的名称(根)和(最后)扩展名。
答案3
将这些命令放入脚本文件中:
#! /bin/bash
current="/your/absolute/path"
rm -rf "${current}"
mkdir -p "${current}"
while read FILENAME; do
ln -s "${FILENAME}" "${current}"
done < <(find "${current}" -printf "%T@ %p\n" | sort -n | tail -3 | cut -d " " -f 2-)
然后:
在您的 crontab 中添加此脚本的执行。