디렉토리를 재귀적으로 탐색하고 마지막 타임스탬프 파일을 검색합니다.

디렉토리를 재귀적으로 탐색하고 마지막 타임스탬프 파일을 검색합니다.

디렉토리 트리에 다음과 같은 타임스탬프가 있다고 가정해 보겠습니다.

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

사용 findperl한 줄:

탭을 사용하여 타임스탬프와 파일 이름을 구분하고 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하고 실행 시 하나 이상의 디렉터리를 인수로 제공합니다.

$ ./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-perl.cpan

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에서는 숨겨진 이름의 일치를 활성화하면서 패턴이 0개의 이름과 일치하도록 허용합니다.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에만 해당됩니다)

관련 정보