다음 단계에 따라 스크립트를 완성해 보세요. (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
.