尝试通过以下步骤完成脚本:(1) 在多个子目录中选择 1gig 最近更改的文件 (2) 最好用于rsync
将文件复制到本地目录 --我rsync
比 cp 更喜欢,因为我可以使用 的功能来rsync
跳过现有文件等。
对于步骤 1,以下内容有效,它为我提供了限制为 1gig 的最新文件
ls -lstrkR /volume1/cctv/* | grep \.mp4$ | awk '
(size += $1) > 1*1024*1024 {exit}
#{print "size=" size "\t" $1 "\t" $6 "\t" $7 " " $8 " "$9 "\t" $10}
{print $10}
'
上面的输出如下所示: file1.mp4 file2.mp4 等。
我没有每个文件的绝对路径,上面的文件来自 /volume1/cctv 的几个子目录(如您所见ls -R
)
我需要: (A)获取上面的输出并通过管道传输到 rsync,或者 (二)对文件执行 cp (但是我可以在没有绝对路径的情况下从此列表中工作吗?)
答案1
该perl
脚本应该执行您想要的操作:给定一个以 NUL 分隔的文件名列表(例如,来自find -print0
),输出最近修改的文件名列表,只要这些文件的总大小不超过 1GB(默认)。您可以在命令行上指定最大大小的演出数量 - 这可以是任何有效的数字、整数或浮点数。
NUL 分隔符意味着这适用于任何文件名,即使它们包含空格或换行符。
$ cat select-newest-one-gig.pl
#! /usr/bin/perl -0
use strict;
my $gigs = shift || 1;
my $maxsize = $gigs * 1024 * 1024 * 1024 ; # 1GB
my $total = 0;
# a hash to contain the list of input filenames and their modtimes
my %filemtimes=();
# hash to contain the list of input filenames and their sizes
my %filesizes=();
# a hash to contain a list of filenames to output.
# use a hash for this so we don't need to write a `uniq` function.
my %outfiles=();
while (<>) {
chomp;
# 7th field of stat() is size in bytes.
# 9th field of stat() is modime in secs since epoch
my ($size,$mtime) = (stat($_))[7,9];
$filesizes{$_} = $size;
$filemtimes{$_} = $mtime;
}
# iterate through the %filemtimes hash in order of reverse mtime
foreach (reverse sort { $filemtimes{$b} <=> $filemtimes{$a} } keys %filemtimes) {
my $size = $filesizes{$_};
# add it to our list of filenames to print if it won't exceed $maxsize
if (($size + $total) <= $maxsize) {
$total += $size;
$outfiles{$_}++;
}
}
# now iterate through the %filesizes hash in order of reverse size
# just in case we can sequeeze in a few more files.
foreach (reverse sort { $filesizes{$b} <=> $filesizes{$a} } keys %filesizes) {
my $size = $filesizes{$_};
if (($size + $total) < $maxsize) {
$total += $size;
$outfiles{$_}++;
}
}
# now print our list of files. choose one of the following, for
# newline separated filenames or NUL-separated.
#print join("\n", sort keys %outfiles), "\n";
print join("\000", sort keys %outfiles), "\000";
将其另存为select-newest-one-gig.pl
并使其可执行chmod +x
。
像这样运行它(例如,最大总文件大小为 10GB):
find /volume1/cctv/ -type f -iname '*.mp4' -print0 | ./select-newest-one-gig.pl 10
这个 perl 脚本可以很容易地修改为采用一个或多个文件扩展名(例如.mp4
)作为参数,然后使用system()
函数调用运行 find 本身并迭代它而不是while (<>)
。将 的输出通过管道传输到其中可能更简单find
- 为什么要重新发明轮子?
以下 perl 脚本将列出(或删除,如果取消注释最后一行)rsync 目标目录中存在的文件不是列在标准输入上。它假定 NUL 分隔的输入,因此即使文件名包含换行符也是安全的。
$ cat unlink-others.pl
#! /usr/bin/perl -0
use strict;
my @files=();
# first arg is target dir, with default
my $targetdir = shift || '/path/to/rsync/target/dir/';
while (<>) {
chomp;
s/^.*\///; # strip path
push @files, quotemeta($_)
}
my $regexp=join("|",@files);
opendir(my $dh, $targetdir) || die "can't opendir $targetdir: $!\n";
my @delete = grep { ! /^($regexp)$/o && -f "$targetdir/$_" } readdir($dh);
closedir $dh;
print join(", ",@delete),"\n";
# uncomment next line if you're sure it will only delete what you want
# unlink @delete
像这样使用它:
find /volume1/cctv/ -type f -iname '*.mp4' -print0 | \
./select-newest-one-gig.pl 10 > /tmp/files.list
rsync --from0 --files-from /tmp/files.list ... /path/to/rsync/target/dir/
./unlink-others.pl /path/to/rsync/target/dir/ < /tmp/files.list
答案2
cd /volume/cctv
echo 0 >/tmp/sztally &&
find .// -name '*.[mM][pP]4' -type f -exec sh -fc '
_cp(){ shift; echo cp "$@$0"; :; }
read sz </tmp/sztally; IFS=/ g=$((1024*1024)); unset _f
for f in $(ls -dkst "$@")
do case $f in
("") f=${2+./${_f%[!0-9. ]*}} _f=${_f##*[pP]4?}
[ 0 -ne "$((g>(sz+${_f%??})))" ] &&
set "$f$@" && sz=$((sz+${_f%??})) _f=;;
(*) [ -z ${_f:+:} ] && set "" ${_f+"$@"}
_f=${_f:+$_f/}$f
esac|| ! _cp "$@" || exit 255
done; _cp "$@"; echo "$sz" >/tmp/sztally
' "/destination/path" {} +
这对我行得通。我在自己的媒体目录中对其进行了测试,它始终仅将最新的 1GB 的 .mp4 文件聚合到一个cp
操作中。我认为ls
您正在寻找的选项是-d
,它将保留所有ls
参数的完整路径名。 Herefind
查找它可以放在一起的所有 .mp4 文件,并按ls
修改时间对其选择进行排序。 shellls
在路径名分隔符 - the - 上分割 的输出/
,因此文件名中的特殊字符不存在问题,因为根本不会考虑特殊字符。
严格来说,-s
选项ls
并不报告文件的大小,而是报告文件的大小已用空间。这两个概念可能不同,但在压缩视频文件的情况下,它们不同的可能性非常小。这实际上并没有按照所写的那样进行复制——它只是echo
操作cp
。如果您测试它并发现它可行,请echo
从_cp()
函数中删除 。
这取决于 POSIX ls
、find
、cp
和sh
。