pipe ls e awk para rsync

2

Tentando concluir um script com as seguintes etapas: (1) selecione 1gig dos arquivos mais recentemente alterados através de um número de subdiretórios (2) preferencialmente usa rsync para copiar os arquivos para o diretório local - Eu prefiro rsync than cp como eu posso usar recursos de rsync para pular arquivos existentes, etc.

para o passo 1, o seguinte funciona e dá-me os mais novos arquivos limitados a 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}
'

A saída do acima é assim: arquivo1.mp4 arquivo2.mp4 etc.

Eu não tenho o caminho absoluto de cada arquivo e os arquivos acima são de vários subdiretórios de / volume1 / cctv (como você pode ver ls -R )

Eu preciso: (a) pegue a saída do acima e canalize para rsync, OR (b) executa o cp nos arquivos (mas posso trabalhar a partir desta lista sem os caminhos absolutos?)

    
por csolad 13.11.2015 / 17:29

2 respostas

2

Este script perl deve fazer o que você deseja: Dada uma lista separada por NUL de nomes de arquivos (por exemplo, find -print0 ), mostre uma lista dos nomes de arquivos modificados mais recentemente, contanto que o tamanho total desses arquivos não excede 1 GB (padrão). Você pode especificar o número de shows para o tamanho máximo na linha de comando - isso pode ser qualquer número válido, número inteiro ou ponto flutuante.

O separador NUL significa que isso funcionará com qualquer nome de arquivo, mesmo que contenham espaços ou novas linhas.

$ 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("
find /volume1/cctv/ -type f -iname '*.mp4' -print0 | ./select-newest-one-gig.pl 10
0", sort keys %outfiles), "
$ 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
0";

Salvar como select-newest-one-gig.pl e torná-lo executável com chmod +x .

Execute-o assim (por exemplo, para um tamanho total máximo de 10 GB):

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

Esse script perl pode ser facilmente modificado para usar uma ou mais extensões de nome de arquivo (por exemplo, .mp4 ) como args e, em seguida, ser executado usando a chamada de função system() e iterar em vez de while (<>) . Provavelmente é mais simples apenas canalizar a saída de find para ele - por que reinventar a roda?

O seguinte script perl listará (ou excluirá se você descomentar a última linha) arquivos que existem no diretório de destino do rsync que não estavam listados no stdin. Ele assume entrada separada por NUL, portanto, é seguro mesmo com nomes de arquivos que contenham novas linhas.

$ 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("
find /volume1/cctv/ -type f -iname '*.mp4' -print0 | ./select-newest-one-gig.pl 10
0", sort keys %outfiles), "
$ 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
0";

Use assim:

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
    
por 14.11.2015 / 10:41
0
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" {} +

Isso funciona para mim. Testei-o no meu próprio diretório de mídia e ele consistentemente agregava apenas o mais novo valor de 1gb de arquivos .mp4 em uma operação cp . Acho que a opção ls que você estava procurando era -d , que manterá caminhos completos para todos os argumentos de ls . Aqui, find procura todos os arquivos .mp4 que podem ser reunidos, e ls ordena suas seleções pelo tempo de modificação. O shell divide a saída de ls nos delimitadores de nome de caminho - o / - e, portanto, não há problemas com caracteres especiais em nomes de arquivos, porque os caracteres especiais nunca são considerados.

Estritamente falando, a opção -s para ls não reporta o tamanho de um arquivo, mas reporta o espaço usado do arquivo. Esses dois conceitos podem diferir, mas no caso de arquivos de vídeo compactados, a probabilidade de eles ultrapassarem o tamanho pequeno. Isso não faz uma cópia como está escrito - é apenas echo a operação cp . Se você testar e achar viável, remova o echo da função _cp() .

Isso depende de um POSIX ls , find , cp e sh .

    
por 21.11.2015 / 08:44

Tags