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).

다음을 수행해야 합니다. (ㅏ)위의 출력을 가져와 rsync로 파이프하거나 (둘)cp 파일(절대 경로 없이 이 목록에서 작업할 수 있나요?)

답변1

perl스크립트는 원하는 작업을 수행해야 합니다. NUL로 구분된 파일 이름 목록(예: from 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여기서는 find함께 모을 수 있는 모든 .mp4 파일을 찾고 ls수정 시간별로 선택 항목을 정렬합니다. 쉘은 ls경로 이름 구분 기호인 - 에 따라 출력을 분할하므로 /파일 이름의 특수 문자는 전혀 고려되지 않으므로 문제가 되지 않습니다.

엄밀히 말하면 -s이 옵션은 ls파일 크기를 보고하지 않습니다.사용 공간. 두 개념은 다를 수 있지만 압축된 비디오 파일의 경우 서로 다를 가능성은 매우 희박합니다. 이것은 실제로 작성된 대로 복사되지 않습니다. 단지 echo작동할 뿐입니다 cp. 테스트하여 작동하는 것으로 확인되면 함수 echo에서 제거하세요._cp()

ls이는 POSIX , find, cp및 에 따라 다릅니다 sh.

관련 정보