ディレクトリを再帰的に参照し、最後のタイムスタンプファイルを検索します。

ディレクトリを再帰的に参照し、最後のタイムスタンプファイルを検索します。

ディレクトリツリーに次のタイムスタンプがあるとします。

root
  |__ parent1
  |      |__ 2021
  |      |     |__ 01
  |      |     |    |__ 22
  |      |     |    |    |__ 12H
  |      |     |    |    |    |__ file1
  |      |     |    |    |    |__ file2
  |      |     |    |    |__ 13H
  |      |     |    |    |    |__ file1
  |      |     |    |    |    |__ file2
  |      |     |    |__ 23
  |      |     |    |    |__ 12H
  |      |     |    |    |    |__ file1
  |      |     |    |    |    |__ file2
  |      |     |    |    |__ 13H
  |      |     |    |    |    |__ file1
  |      |     |    |    |    |__ file2
  |__ parent2
  |      |__ etc
                         

私が望むのは、このフォルダ構造を再帰的に進め、各フォルダなどにparent1見つかったparent2最新のタイムスタンプを含むファイルの数と一緒に表示することです。たとえば、次のようになります。

 PARENT  |     LAST_TIMESTAMP    |  COUNT  |
--------------------------------------------
parent1  |  2021-01-23T13:00:00  |    2    |
parent2  |  2022-01-01T00:00:00  |    5    | (dummy example)
  ...             ...                ...

他の答えも見てきましたが、すべてすべてのフォルダにあるファイルの変更日のみを考慮します。この場合は、フォルダ名にのみ関連します。

答え1

使用findperl1行:

タブを使用してタイムスタンプとファイル名を区切り、NULを使用して各レコードを区切ります。したがって、改行文字を含むファイル名を含むすべてのファイル名で機能します。

find .. -type f -printf '%T@\t%p\0' | 
    perl -MDate::Format -0ne '
      ($t,$f) = split /\t/,$_,2;
      (undef,$p) = split "/", $f;

      $T{$p} = $t if ($t > $T{$p});
      $count{$p}++;

      END {
        my $fmt = "%-20s | %-19s | %5s |\n";
        printf "$fmt", "PARENT", "LAST_TIMESTAMP", "COUNT";
        print "-" x 52, "\n";

        foreach (sort keys %T) {
          printf $fmt, $_, time2str("%Y-%m-%dT%H:%M:%S",$T{$_}), $count{$_}
        }
      }'

次の出力が生成されます。

PARENT               | LAST_TIMESTAMP      | COUNT | 
---------------------|---------------------|-------|
foo                  | 2021-07-16T22:54:22 |     4 | 
bar                  | 2021-06-29T12:25:06 |    13 | 
baz                  | 2021-07-14T14:31:43 |     5 | 
quux                 | 2021-07-16T19:46:21 |     7 | 

またはPerlを使用している場合ファイル::検索findモジュールでは、出力をパイプで接続する必要はありません。

#!/usr/bin/perl

use strict;
use Date::Format;
use File::Find;

my %T;     # hash containing newest timestamp for each top-level dir
my %count; # count of files in each top-level dir

find(\&wanted, @ARGV);

my $fmt  = "| %-20s | %-19s | %5s |\n";
my $hfmt = "|-%-20s-|-%-19s-|-%5s-|\n";

#print "-" x 54, "\n";

printf "$fmt", "PARENT", "LAST_TIMESTAMP", "COUNT";
printf $hfmt, "-" x 20, "-" x 19, "-" x 5;

foreach (sort keys %T) {
  printf $fmt, $_, time2str("%Y-%m-%dT%H:%M:%S", $T{$_}), $count{$_}
}

#print "-" x 54, "\n";

sub wanted {
  return unless -f $File::Find::name;

  # uncomment only one of the following statements:

  # get the mod time of the file itself
  my $t = (stat($File::Find::name))[9];
  # get the mod time of the directory it's in
  #my $t = (stat($File::Find::dir))[9];

  my $p = $File::Find::dir;
  $p =~ s:^\.*/::;

  $T{$p} = $t if ($t > $T{$p});
  $count{$p}++;
};

たとえば、find-latest.plmake実行可能ファイルを使用してそれを保存し、chmod +x find-latest.pl実行時に引数として1つ以上のディレクトリを指定します。

$ ./find-latest.pl ../
| PARENT               | LAST_TIMESTAMP      | COUNT |
|----------------------|---------------------|-------|
| foo                  | 2021-07-16T22:54:22 |     4 |
| bar                  | 2021-06-29T12:25:06 |    13 |
| baz                  | 2021-07-14T14:31:43 |     5 |
| quux                 | 2021-07-16T19:46:21 |     7 |

これはパールが必要です日付形式 基準寸法。 Debianではapt-get install libtimedate-perlcpan

strftime()または、Perlに含まれるコアモジュールであるPOSIXモジュールの機能を使用することもできます。

File::Findまた、Perlに含まれるコアPerlモジュールです。

答え2

ディレクトリ階層の形式が次のとおりです。

cd root &&\
find . -type d ! -name . -path '*/*/*/*/*/*' |
sort -rt/ |
perl -sF/ -lane '$,=" | ";
  print qw(PARENT LAST_TIMESTAMP KOUNT) if $.==1;
  my $fc = -1+ split /\n/, qx(ls -l $_);
  my $parent = $F[1];
  !$seen{$parent}++ && do{
    my($dt, $tm) = ("@F[2..4]", $F[5]);
    my $timestamp = sprintf "%sT%s%s", $dt, $tm =~ s/H$//r,  (":00" x 2); 
    print $parent, $timestamp, $fc;
  };
' -- -\"=- -|column -t|
sed -e '1!b;h;s/./-/gp;x;G'

出力:-

----------------------------------------
PARENT  |  LAST_TIMESTAMP       |  KOUNT
----------------------------------------
pA      |  2021-03-16T23:00:00  |  6

答え3

を使用すると、ワイルドカードパターンの修正タイムスタンプに基づいてソートされたディレクトリのzsh一般的なファイルのリストを取得できます。$topdir

$topdir/**/*(.NDom)

ワイルドカード修飾子は、通常の(.NDom)ファイル()の結果パス名のリストが.変更タイムスタンプ()に基づいてソートされるようにします。修飾子のとはシェルオプションのとやや似たように動作しますが、この単一パターンの場合、Internet Explorerは隠された名前の一致を有効にしながらパターンがゼロの名前と一致することを許可します。omNDnullglobdotglobbashND

以下のスクリプトでは、次を使用します。

#!/bin/zsh

zmodload -F zsh/stat b:zstat

printf '| %-20s | %-20s | %5s |\n' PARENT LAST_TIMESTAMP COUNT
printf '| %-20s | %-20s | %5s |\n' '' '' '' | tr ' ' '-'

for topdir do
        files=( $topdir/**/*(.NDom) )
        if (( ${#files} > 0 )); then
                timestamp=$( zstat -F '%Y-%m-%dT%H:%M:%S' +mtime $files[1] )
        else
                timestamp=N/A
        fi

        printf '| %-20s | %-20s | %5s |\n' $topdir $timestamp ${#files}
done

スクリプトは、zshコマンドラインで次のディレクトリパスセットを使用します。

$ ./script parent*/

...parent*/トップレベルのディレクトリ名が一致する場所です。

単純なヘッダーを印刷してから、指定されたディレクトリパスを繰り返します。

各パスにワイルドカードパターンを使用して、最後に変更されたタイムスタンプに基づいてソートされた一般ファイル(隠し名前を含む)のパス名のリストを取得します$topdir/**/*(.NDom)

このリストが空でない場合zstat(組み込みのロード可能なシェル)を使用して、最近変更されたファイルのタイムスタンプを抽出するか、N/Aファイルが存在しない場合は文字列に設定します。

現在のディレクトリ、タイムスタンプ、およびファイルの数が表形式で印刷されます。

例を実行してください:

$ ./script ~me/{Documents,Work,admin}/
| PARENT               | LAST_TIMESTAMP       | COUNT |
|----------------------|----------------------|-------|
| /home/me/Documents/  | 2021-06-18T13:27:39  |   816 |
| /home/me/Work/       | 2021-06-22T10:57:49  |  2582 |
| /home/me/admin/      | 2021-07-14T11:13:30  |   191 |

ここで使用されるテーブル形式は有効なマークアップであり、例のテーブルは次のように表示されます。

LAST_TIMESTAMP 計算
/ホーム/私/文書/ 2021-06-18T13:27:39 816
/家/私/職場/ 2021-06-22T10:57:49 2582
/家/私/管理者/ 2021-07-14T11:13:30 191

bashシェルでは、$topdir次のように、ターゲットディレクトリの下にあるディレクトリ構造内で最近変更された一般的なファイルを見つけることができます。

shopt -s nullglob dotglob globstar

unset newest
for name in "$topdir"/**/*; do
        if [ -f "$name" ] && [ ! -h "$name" ]; then
                if [[ "$name" -nt "$newest" ]]; then
                        newest=$name
                fi
        fi
done

これは-ntテストを使用してからbash最後に変更されたファイルを追跡します$newest。現在のファイルがシンボリックリンクではなく通常のファイルである場合、-f否定テスト-hはtrueです。

上記と同じですが、bashシェル用に書かれています。

#!/bin/bash

shopt -s nullglob dotglob globstar

printf '| %-20s | %-20s | %5s |\n' PARENT LAST_TIMESTAMP COUNT
printf '| %-20s | %-20s | %5s |\n' '' '' '' | tr ' ' '-'

for topdir do
        unset newest
        count=0
        for name in "$topdir"/**/*; do
                # Test whether "$name" is a regular file
                # and not a symbolic link.
                if [ -f "$name" ] && [ ! -h "$name" ]; then
                        count=$(( count + 1 ))
                        if [[ "$name" -nt "$newest" ]]; then
                                newest=$name
                        fi
                fi
        done

        if [ -n "$newest" ]; then
                printf -v timestamp '%(%Y-%m-%dT%H:%M:%S)T' "$(stat -c %Y "$newest")"
        else
                timestamp=N/A
        fi

        printf '| %-20s | %-20s | %5s |\n' "$topdir" "$timestamp" "$count"
done

OpenBSD では以下を使用します。

timestamp=$( stat -f %Sm -t '%Y-%m-%dT%H:%M:%S' "$newest" )

変える

printf -v timestamp '%(%Y-%m-%dT%H:%M:%S)T' "$(stat -c %Y "$newest")"

このスクリプトでは(後者はLinux専用です)

関連情報