我希望从特定目录中获取用于在 bash 中将文件从最新文件合并到最旧文件的命令。这意味着日期较新的文件将保存在日期较旧的文件之前
答案1
在zsh
shell 中,通配模式和通配限定符*(.om)
将扩展到当前目录中常规文件的所有名称,并按其修改时间戳排序。最近修改的文件位于结果列表中的第一个。如果该目录没有任何常规文件,该模式会在 shell 中生成错误。
zsh
因此,在外壳中,
cat ./*(.om) >Save.txt
或者,对于数千个文件,使用循环,
for name ( ./*(.om) ) cat $name >Save.txt
从以下位置调用此命令bash
:
zsh -c 'for name ( ./*(.om) ) cat $name >Save.txt'
您还可以使用zargs
in 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 -printf
option)、sort
and sed
(for the -z
option) 和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
以删除文件名之前的时间戳,最后输送到xargs
从cat
STDIN 获取的所有文件名。输出被重定向到merged.txt
.
顺便说一句,如果您使用 FreeBSD 或 Mac,FreeBSDfind
也支持并且其支持-printf
版本及其具有.不幸的是,他们的 sed 版本不支持,所以你必须使用其他东西 -将是一个很好的替代品,因为它和选项使它的工作方式非常类似于.例如,使用以下内容代替上面的管道:sort
-z
xargs
-0
-z
perl
-p
-n
sed
sed
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”。本质上,这是cat
Perl的重新实现。
更高级的版本会采用一个-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 -- *
注意: 、grep
、stat
和sort
上面是内置的 perl 函数,不是命令行实用程序。您可以通过 获取有关它们的详细信息perldoc -f
。