在 AIX 系统上使用 find 命令查找前 N 个最旧的文件(无需 printf)

在 AIX 系统上使用 find 命令查找前 N 个最旧的文件(无需 printf)

我正在尝试找到一个类似于下面使用的解决方案,从给定目录开始,挖掘 AIX 系统上最旧的前 N ​​个文件(修改时间),并挖掘其下的所有子目录。不幸的是,printfAIX(我的版本是 7.1)不支持 find 命令。有没有其他方法可以在 AIX 上完成相同的任务?

$ find /home/sk/ostechnix/ -type f -printf '%T+ %p\n' | sort | head -n 5

来源:https://ostechnix.com/find-oldest-file-directory-tree-linux/

find命令的 AIX 手册页

答案1

这是一个 POSIX 解决方案。

stat可以提供帮助,但 POSIX 并不要求这样做。一般来说,POSIX 工具无法完全取代stat. 解析ls -l以获取 mtime 并非一项简单的任务。

唯一相对简单的方法是find -newer

# parameters (adjust them)
set -- /home/sk/ostechnix/ /another/starting/point "/and another/"
N=5

# fixed code (nothing to adjust)
find "$@" -type f -exec sh -c '
   f="$1"
   shift
   c="$(find "$@" -type f ! -newer "$f" | wc -l)"
   printf "%s\\t%s\\n" "$c" "$f"
' find-sh {} "$@" \; | sort -k 1n,1 | head -n "$N" | cut -f 2-

对于每个文件,代码都会查找并计算不再更新的文件(就 mtime 即修改时间而言)。其余的是sort … | head … | cut …

笔记:

  • 对于包含换行符的路径名,此操作将会失败。

  • 如果在代码运行时添加、删除或修改文件,则结果不可信。

  • 该解决方案不能很好地适应文件数量。我认为最优。开始在最多包含几百个文件的目录上进行测试。我能想到一两种可以更好地扩展的方法,但它们相当复杂,你最好只编译find支持 的GNU -printf

  • 看起来,find当内部文件发现比当前文件更旧的文件数量足够多时,我们可以中断它,因为在某个数量下,我们可以确定该文件不可能是N最旧的文件,对吗?但这! -newer意味着“更旧或同样旧”,并且可能有任意多个文件同样旧。我对此进行了测试。一个“优化”:

    c="$(find … | head … | wc -l)"
    

    可以显著加快速度,但当文件同样老旧时,结果可能会出错。我不会详细说明。我认为如果find提供类似-older严格较旧,不一样! -newer),我们可以通过这种方式进行优化。

  • find-sh解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh

  • 代码支持多个起始路径。请记住,如果同一个文件出现在两个(或更多)起始路径下,那么它将被视为find两个(或更多)文件(例如,cd /dev && find ./ /dev /dev/null /dev//null | grep /null打印四行引用同一个文件)。在这种情况下,N 个最旧文件中的两个或多个可能是同一个文件。您找到的命令在这方面是类似的。指定不重叠的路径是正确的。

  • 如果你只想指定一个起始路径,那么你可以加快速度一点通过使用这个修改后的代码:

    # parameters (adjust them)
    start=/home/sk/ostechnix/
    N=5
    
    # fixed code (nothing to adjust)
    find "$start" -type f -exec sh -c '
       start="$1"
       shift
       for f do
          c="$(find "$start" -type f ! -newer "$f" | wc -l)"
          printf "%s\\t%s\\n" "$c" "$f"
       done
    ' find-sh "$start" {} + | sort -k 1n,1 | head -n "$N" | cut -f 2-
    

    这里我们通过将多个结果从外部传递find到 shell 来生成更少的 shell。在之前的代码中,我们无法轻松使用此技巧,因为我们需要传递任意数量的起始路径。

  • 在子 shell 中运行代码,因此它不会影响主 shell 中的任何内容($N、、、位置参数或)。$f$c$start

  • 通常find运行很多次;它会测试同一组文件(但测试并不完全相同)。如果存在类似这样的问题,permission denied它们将多次出现。考虑将 stderr 重定向到/dev/null,至少对于内部finds ( c="$(find … 2>/dev/null | …)")。

答案2

当面临此类限制时,我通常会求助于 perl,它通常安装在 AIX 系统上。文件::查找模块有助于完成繁重的工作。下面的脚本使用该模块来发现文件,就像find会一样,并在过程中捕获修改时间戳,使用 perl 的stat()功能。一旦收集了文件,它就会将结果缩减为给定的“N”,并按最早的修改日期排序。如果您想模仿在find ... printf '%T+ %p\n'文件名旁边打印时间戳的行为,我已经拆分出一行代码来删除时间戳。

使用 perl 比使用 shellcode 解决方法有优势,因为:

  • 它不会因文件名中包含空格(尤其是换行符)或其他转义字符而阻塞
  • 它不需要 GNU 日期程序的支持
  • 它不依赖于解析 IBM 的输出istat 程序

输出文件名时可能会造成混淆;在这里,我用换行符分隔它们,但请注意,包含换行符的文件名在视觉上会被误认为是附加文件。

我绝不是 perl 专家,因此代码有点“蛮力”和简单,但面对未来的维护问题,我欣赏明显的简单性,除非性能或内存限制是一个问题。请注意,当前脚本需要足够的内存来将所有文件名和时间戳存储为字符串数组,以及请求的尽可能多的排序结果。

#!/usr/bin/perl -w
# prints N oldest files

use strict;
use File::Find ();

# expect at least 2 arguments: N and 1 or more starting directories;
# $#ARGV is "number of arguments minus one", counting from [0]
if ($#ARGV < 1) {
  die "Usage: $0 N dir1 ..."
}

my $n = shift;
unless ($n =~ /^\d+$/ && $n > 0) {
  die "$0: N must be a positive integer"
}

my @results = ();

sub wanted {
  return unless -f $_;
  push (@results, (stat($_))[9] . " " . $_);
}

# using "no_chdir=1" so that stat() and $_ have the full filepath
File::Find::find({wanted => \&wanted, no_chdir => 1}, @ARGV);

# array is zero-based, so subtract one...
--$n;

# keep N within the range of the results
$n = $#results if $n > $#results;

# a plain numeric sort works with the age-in-seconds as the leading data;
# files with the same timestamp will then sort on filename
my @oldest = (sort @results)[0..$n];

# strip the timestamp out
@oldest = map { $_ =~ s/^\d+ //; $_; } @oldest;

print join("\n", @oldest) . "\n";

答案3

此公式可能适用于 AIX:

find /home/sk/ostechnix/ -type f| while read line; do echo "$(date +%s -r "$line") $line"; done|sort -n -k1|cut -d' ' -f2-

来源

答案4

在搜索网络之后,我主要根据这里提供的答案提出了以下一行解决方案:Unix/Linux 按修改日期查找和排序Perl 单行,打印文件名作为输出的一部分

以下解决方案首先使用find命令查找文件(从当前目录开始),然后将输出通过管道传输到perl命令进行排序,然后将结果文件列表通过管道传输到另一个perl命令,以所需格式获取每个文件的时间戳。结果将显示前 5 个最旧的文件。

我不是 Perl 专家,但我猜下面的内容可以进一步简化。如果是这样,请告诉我。到目前为止,下面的解决方案似乎运行良好。它在我的 AIX 系统上运行良好。

find . -type f -print  2>/dev/null |
perl -l -ne '
$_{$_} = -M;  
END {
    $,="\n";
    @sorted = sort {$_{$b} <=> $_{$a}} keys %_;  
    print @sorted[0..5];
}'  | xargs -I {} perl -MPOSIX   -e 'print "\n $ARGV[0] -------> $1 " . strftime("%A %Y-%m-%d  %H:%M:%S", localtime((stat "$ARGV[0]")[9]))  '  {}

输出如下:

./file1.txt ---> Sunday 2018-03-04 15:20:32
./sample/file2.sh ---> Sunday 2019-01-27 08:30:45
./test/file3.txt ---> Tuesday 2019-05-21 18:45:32
./sample/temp/file4.sh ---> Friday 2019-12-27 12:30:45
./file5.txt ---> Tuesday 2020-06-13 15:20:32

相关内容