n個の最新のファイルを特定のディレクトリにコピーする方法は?

n個の最新のファイルを特定のディレクトリにコピーする方法は?

使用

find $dirname -printf "%T@ %Tc %p\n" | sort -n | tail -n 15

以下のディレクトリ構造では、n番目(例ではn = 15)の最新のファイルを見つけることができます$dirname。これで、ファイルをディレクトリにコピー(またはリンク)したいと思います$current。新しく追加されたすべてのファイルを表示するために定期的にバックグラウンドスクリプトを実行したいと思います。

これの背景は、私がrclonems共有ポイントから私のラップトップにディレクトリ構造をミラーリングしてから、$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 -nandを使用しました。入力全体を読み取る必要がありますが、最初の15個のファイル名を出力してすぐに終了できます。head -ntail -nheadtail

xargs -0r2回使用してください。まず、readlink各ファイル名の完全な標準パスを取得して$currentからln

find注:これには、サポートされているバージョン、サポートされているバージョン、、および、ターゲットディレクトリオプションをサポートするバージョン、およびprintfサポートされているバージョンが必要です。たとえば、これらすべてのGNUバージョンがあります。sortheadreadlinksed-zln-txargs-0

また、他のバージョンの sed では for tab が角かっこ式内で動作するかどうかは覚えていませんが、\tGNU sed では確実に動作します。

Linuxを実行している場合は、coreutils、findutils、sedなどのGNUバージョンのみが必要です。 (GNU以外のバージョンをインストールするなど、本当に奇妙で説明できないクレイジーなことをしない限り)

答え2

zshでは、多くの複雑なfind呼び出しが秘密ですが短いものに変換されます。 zshのカスタムソートは他の方法と比較して特に便利です。次のzshコマンドは、次の15の最新の一般ファイルを一覧表示します$dirname

print -rC1 -- $dirname/**/*(.Nom[1,15])

**する再帰ワイルドカード。最後の括弧内の文字は次のとおりです。グローバル予選:一致するファイルが存在しないときにエラーを防ぐために.通常のファイルを選択し、修正時間順(最近のファイルから)に並べ替えて、上位15個のファイルを選択します。Nom[1,15]

これはコマンドラインのワイルドカード拡張であるため、ファイル名のシェル特殊文字について心配する必要はありません。で始まる場合は、$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歴史拡張修飾子ファイル名から、それぞれデフォルト名(tail)、拡張子のない名前(root)、および(最後の)拡張子を抽出します。

答え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にこのスクリプトの実行を追加します。

関連情報