通过管道 ls 和 awk 到 rsync

通过管道 ls 和 awk 到 rsync

尝试通过以下步骤完成脚本:(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 lsfindcpsh

相关内容