合并最近的文件

合并最近的文件

我希望从特定目录中获取用于在 bash 中将文件从最新文件合并到最旧文件的命令。这意味着日期较新的文件将保存在日期较旧的文件之前

答案1

zshshell 中,通配模式和通配限定符*(.om)将扩展到当前目录中常规文件的所有名称,并按其修改时间戳排序。最近修改的文件位于结果列表中的第一个。如果该目录没有任何常规文件,该模式会在 shell 中生成错误。

zsh因此,在外壳中,

cat ./*(.om) >Save.txt

或者,对于数千个文件,使用循环,

for name ( ./*(.om) ) cat $name >Save.txt

从以下位置调用此命令bash

zsh -c 'for name ( ./*(.om) ) cat $name >Save.txt'

您还可以使用zargsin zsh,它是 的一种内置变体xargs

autoload -U zargs
zargs -- ./*(.om) -- cat -- >Save.txt

bash

zsh -c 'autoload -U zargs; zargs -- ./*(.om) -- cat -- >Save.txt'

答案2

您可以简单地执行此操作,假设我们有这些文件:

$ cat a.txt 
a
$ cat b.txt 
b
$ cat c.txt 
c
$ ls -lt *.txt
-rw-rw-r-- 1 user user 2 oct  7 09:21 a.txt
-rw-rw-r-- 1 user user 2 oct  7 09:21 b.txt
-rw-rw-r-- 1 user user 2 oct  7 09:21 c.txt

然后我们运行这个命令:

$ ls -1t *.txt | xargs -I {} cat "{}" > Save.txt
$ cat Save.txt 
a
b
c
  • ls -1t仅列出文件的名称。
  • xargs -I {} cat "{}"cat对作为参数传递的每个文件执行 a 。

还有一个重要的注意事项:为什么不是解析ls(以及该怎么做)?

答案3

有很多方法可以做到这一点,但如果您想坚持只使用 shell 语法和常用实用程序,最好的方法之一是使用find(for the -printfoption)、sortand sed(for the -zoption) 和xargs(for -0)的 GNU 版本:

find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
  sort -z -r -n -k 1,1 |
  sed -z -e 's/^[^\t]*\t//' |
  xargs -0r cat > merged.txt

这适用于包含以下内容的文件名任何有效字符,包括空格、制表符、换行符以及 shell 使用的字符,例如;<>|&- 唯一可以使用的字符不是文件名中有效的是 NUL 字符,这就是为什么它被用作文件名分隔符(以及为什么它是唯一可靠的文件名分隔符)。

find 命令输出当前目录中的所有文件名,前缀为修改时间(自纪元以来的秒数)%T@和制表符%t,然后是文件名本身和 NUL 字符 - 这实际上是-print0时间戳和文件名的增强。该-maxdepth 1选项将其限制为仅当前目录 - 即告诉它不要递归到子目录。

然后将其通过管道输送到其中,sort以按时间戳对文件名进行反向排序,然后输送到其中sed以删除文件名之前的时间戳,最后输送到xargscatSTDIN 获取的所有文件名。输出被重定向到merged.txt.


顺便说一句,如果您使用 FreeBSD 或 Mac,FreeBSDfind也支持并且其支持-printf版本及其具有.不幸的是,他们的 sed 版本不支持,所以你必须使用其他东西 -将是一个很好的替代品,因为它和选项使它的工作方式非常类似于.例如,使用以下内容代替上面的管道:sort-zxargs-0-zperl-p-nsedsed

perl -0 -p -e 's/^[^\t]*\t//'

或者只安装 GNU sed

顺便说一句,没有什么特别的理由不在 linux 上使用 perl - 只是 sed 更小、更简单,并且启动开销比 perl 略少......在现代系统上这是微不足道的差异。


或者,您可以在以下位置完成整个操作perl

$ perl -e '@ARGV = sort { (stat($b))[9] <=> (stat($a))[9] } @ARGV;
    while (<>) {
      if ($ARGV eq "merged.txt") { close(ARGV); next } ; # skip to next file
      print
    }' -- * > merged.txt

在此,perl 按时间戳对其文件名参数进行排序(使用其内置stat函数,该函数返回一个以修改时间戳作为第 10 个元素的数组,因此我们使用它,[9]因为 perl 数组从 0 而不是 1 开始。参见perldoc -f stat),然后打印它们out....排除重定向目标“merged.txt”。本质上,这是catPerl的重新实现。

更高级的版本会采用一个-o outputfile选项或类似的选项并打开自己的输出文件(并在排序之前从 @ARGV 中删除输出文件名 - 如果它已经存在并且与 glob 匹配*),那么它不需要硬 -为输出文件编写排除代码。

#!/usr/bin/perl

use Getopt::Std;

getopts('o:', \%opts);
$opts{o} = '/dev/stdout' unless defined($opts{o}); # default to stdout
# alternatively, you could print an error message to STDERR and exit:
# die "-o option is required\n" unless defined($opts{o});

@ARGV = grep { ! /^$opts{o}$/ } @ARGV;
@ARGV = sort { (stat($b))[9] <=> (stat($a))[9] } @ARGV;

open($out,">",$opts{o});
while (<>) {
  print $out $_;
};
close($out);

您可以将其保存在 $PATH 中的某个位置(您不希望它位于当前目录中,否则它将包含在输出中 - 有一些方法可以避免这种情况,但它们会使脚本更长一点比简单示例中所需的更复杂),例如,使用 使其可执行chmod,并将其运行为:

merge.pl -o merged.txt -- *

注意: 、grepstatsort上面是内置的 perl 函数,不是命令行实用程序。您可以通过 获取有关它们的详细信息perldoc -f

相关内容