如何将n个最新的文件复制到特定目录?

如何将n个最新的文件复制到特定目录?

使用

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 -nandhead -n而不是,tail -n因为它会稍微快一些 -head可以在输出前 15 个文件名后立即退出,而tail必须读取整个输入。

xargs -0r使用两次。首先用于readlink获取每个文件名的完整规范路径,然后$current使用ln.

find注意:这需要支持的版本,以及、、和printf的版本sortheadreadlinksed支持,以及支持目标目录选项-z的版本,以及支持的版本。例如所有这些的 GNU 版本。ln-txargs-0

另外,我不记得\tfor 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 中添加此脚本的执行。

相关内容