크기가 10MB 미만인 mp4 파일이 포함된 모든 디렉터리를 반복적으로 찾으려고 합니다.
요구 사항은,
- 디렉토리에는 mp4 파일이 하나만 있을 수 있습니다.
- mp4 파일은 10MB를 초과할 수 없습니다.
내가 사용하는 명령은
% find . -type f -name "*.mp4" -size -10M | cut -d/ -f2 | sort | uniq -c | grep "^ 1"
무슨 일이 일어나고 있는지 잘 모르겠지만 이 명령은 정확한 결과를 반환하지 않습니다.
추가 조사 결과 다음 명령이 작동하는 것으로 나타났습니다.
find . -type 'f' -name "*.mp4" -printf '%h\n' | sort | uniq -c | grep -E "\s+1\s"| cut -c 9-
그런데 믹스에 추가해보니 -size -10000000c
해당 파일에 10MB 미만의 mp4 파일이 1개 있고, 10MB가 넘는 다른 mp4 파일도 있는 것으로 나타났습니다. 내 말은 내가 언급한 명령은 크기가 10MB보다 큰 mp4 파일을 고려하지 않는다는 것입니다. 이 질문은 두 단계로 나눌 수 있다고 생각합니다.
mp4 파일이 포함된 모든 디렉터리를 찾습니다. 이는 위의 명령으로 수행됩니다.
파일이 10MB보다 작은지 확인하세요.
다음 명령을 사용하여 디렉터리에 있는 단일 mp4 파일의 파일 크기를 얻을 수 있습니다.
find . -type 'f' -name "*.mp4" -printf '%h\n' | sort | uniq -c | grep -E "\s+1\s" | cut -c 9-| xargs -I {} -n 1 /usr/bin/du -a "{}" | grep -v ".mp4$"
답변1
find
적어도 GNU의 경우 -size -10M
이는 다음 메가바이트로 반올림된 크기가 10보다 작은(즉, 9 이하) 파일의 경우에 해당됩니다.
크기가 9 x 1024 x 1024 + 1 = 9437185바이트인 파일은 10MiB로 반올림되어 10 미만이 아니므로 선택되지 않습니다.
10MB(1MB는 1,000,000바이트, 1메비바이트 == 1,048,576바이트와 혼동하지 마세요)보다 엄격하게 작아서 크기가 0~9,999,999인 파일의 경우 다음을 사용하세요.
find . -size -10000000c
10MiB보다 엄격하게 작은 파일의 경우 크기 범위는 0에서 10485759입니다.
find . -size -10485760c
이제 GNU 시스템에서 이러한 파일 중 하나만 포함하는 디렉토리를 얻으려면 다음을 수행할 수 있습니다.
LC_ALL=C find . -name '*.mp4' -type f -size -10000000c -printf '%h\0' |
LC_ALL=C sort -z |
LC_ALL=C uniq -zu |
tr '\0' '\n'
어디
find
h
이러한 파일의 ead(디렉토리 이름)를 NUL로 구분하여 인쇄합니다 ( 현재 로케일에서 유효한 텍스트가 아니더라도LC_ALL=C
다른 방식으로 끝나는 모든 파일 이름은 보고됩니다 )..mp4
sort
이를 정렬합니다uniq
(LC_ALL=C
파일 이름이 로케일에서 유효한 텍스트가 아닌 문제 및 문자가 완전히 정의된 순서가 아닌 문제를 피하기 위해 다시 한 번).uniq -zu
고유한 항목만 보고됩니다.
NUL은 파일 경로에 나타날 수 없는 유일한 문자이기 때문에 파일 목록은 NUL로 구분되어 전달됩니다. 우리는 이러한 NUL을 사람이 사용할 수 있도록 개행 문자로 변환하게 됩니다 tr
.
를 사용하면 zsh
다음 작업도 수행할 수 있습니다.
print -rC1 -- **/*(NFe['()(( $# == 1 )) $REPLY/*.mp4(N.L-10000000Y2)'])
어디:
print -rC1 --
print
그것은 olumnr
에 대한 논쟁입니다.1
C
**/
하위 디렉터리 수에는 제한이 없습니다.*(NF...)
임의의 파일 이름(숨겨진 파일 제외)이지만 ,N
,F
... glob 한정자로e
추가로 한정됩니다 .N
:nullglob
불일치 시 오류를 반환하는 대신 null로 확장되도록 이 glob을 활성화합니다.F
: 모든 디렉토리(F
및를 제외하고 하나 이상의 항목이 있는 디렉토리)를 선택합니다..
..
e[code]
:성공적인 파일을 선택합니다code
.() {body} arguments
여러 매개변수가 있는 익명 함수입니다.- 반환 된 산술 평가
{body}
입니다 .(( $# == 1 ))
진짜이 익명 함수의 매개변수 개수가 1인 경우. $REPLY
내부에는code
고려 중인 파일(여기서는 디렉터리)의 경로가 있습니다.*.mp4(qualifiers)
: (숨겨지지 않은)mp4
파일이 추가로 한정됩니다..
: 일반 파일만(예:find
's'-type f
)L-10000000
: 파일 크기는 10MB보다 엄격하게 작습니다.Y2
: 2개의 파일을 찾은 후 최적화를 중지합니다.
.
(현재 작업 디렉터리 자체)은 고려되지 않습니다 . 이를 고려하고 싶다면 **/*
로 바꾸십시오 {.,**/*}
.
이제 명확하게 설명했듯이 mp4 파일이 하나만 포함된 디렉터리를 찾고 해당 파일이 일반 파일(디렉토리, 심볼릭 링크가 아님...)이고 크기가 10MB 미만인 경우(예를 들어 를 포함하는 디렉터리 제외): 5MB 및 15MB mp4 파일(크기에 관계없이 총 여러 개의 mp4가 있으므로 10MB보다 작은 mp4 파일이 하나만 있음에도 불구하고) 여전히 다음과 같습니다 zsh
.
print -rC1 -- **/*(NFe['
() {
(( $# == 1 )) && ()(($#)) $1(N.L-10000000)
} $REPLY/*.mp4(NY2)
'])
GNU find
및 GNU awk
(또는 NUL로 구분된 레코드를 처리할 수 있는 awk)의 경우 다음과 같습니다.
LC_ALL=C find . -name '*.mp4' -printf '%h\0%s\0%y\0' |
awk -v RS='\0' '
{
getline size; getline type
total[$0]++
if (size < 10e6 && type == "f") found[$0]++
}
END {for (dir in found) if (total[dir] == 1) print dir}'
답변2
find
훌륭해요. 저는 이보다 훨씬 더 복잡한 작업에 항상 이 프로그램을 사용합니다... 하지만 때로는 모든 find
옵션을 파악하고 원하는 작업을 수행하도록 한 다음, sort
등과 같은 다른 프로그램을 사용하는 것이 좋을 때도 있습니다. PITA의 경우 디렉토리를 재귀적으로 검색하기 위한 괜찮은 라이브러리가 있는 언어로 원하는 작업을 수행하는 사용자 정의 도구를 직접 작성하고 이를 수행하려면 쉘 명령줄 편집기 대신 괜찮은 편집기를 사용하는 것이 더 쉬운 것 같습니다.grep
uniq
따라서 다음과 같은 또 다른 작은 변형을 작성하게 됩니다. wanted
서브루틴을 변경하면 find
함수가 발견하는 내용이 변경됩니다. 그러면 파일 이름이 다음으로 끝나는 크기 <= 10MiB의 일반 파일이 하나 이상 포함된 디렉터리 목록이 인쇄됩니다 .mp4
.
$ cat find-mp4-1.pl
#!/usr/bin/perl
use strict;
use File::Find;
my %found;
sub wanted {
-f $_ && -s $_ <= 10485760 && /\.mp4\Z/s &&
$found{$File::Find::dir . "/"}++;
};
# Search all directories listed on command line.
# Default to current directory
find(\&wanted, @ARGV ? @ARGV : '.');
print join("\n", sort keys %found), "\n" if %found;
나는 이와 같은 작은 스크립트를 너무 많이 작성해서 File::Find
셀 수 없을 만큼 많이 작성했습니다.
실행 예시:
$ mkdir videos
$ touch video1.mp4 videos/video2.mp4
$ ./find-mp4-1.pl
./
./videos/
그러면 때로는 NUL로 구분된 출력을 사용하는 것이 유용하므로 -0
옵션이 필요하다는 것을 깨닫게 됩니다. 이 작업이 완료되면 명령줄에서 원하는 크기를 지정할 수 있으면 좋을 것이라고 생각했습니다. 파일 이름 패턴 일치에도 동일하게 적용되며 대소문자를 구분하지 않는 검색 옵션이 있으면 좋을 것입니다. 따라서 " 사람이 읽을 수 있는" 크기, I 정규식을 미리 컴파일하고 파일 이름의 기본 이름 부분만 일치시키면(조금 조기 최적화를 좋아하지 않는 사람) 조금 더 빠르게 만들 수 있습니다. 그리고... 정신을 차리고 다음을 수행하십시오.
$ cat find-mp4-2.pl
#!/usr/bin/perl
use strict;
use File::Find;
use Number::Bytes::Human qw(parse_bytes);
use Getopt::Std;
my %found;
my %opts;
$Getopt::Std::STANDARD_HELP_VERSION=1;
our $VERSION='0.2';
getopts('0:s:r:i',\%opts) ||
die "Usage: $0 [-0] [-s size] [-r regex] [-i] [directory...]\n";
my $sep = $opts{0} ? "\0" : "\n";
my $size = $opts{s} // '10MiB';
my $regex = $opts{r} // '\.mp4\Z';
$size = parse_bytes($size);
# pre-compile the regex: case insensitive or case sensitive?
$regex = $opts{i} ? qr/$regex/si : qr/$regex/s;
sub wanted {
-f $_ && -s $_ <= $size && $File::Find::name =~ /$regex/ &&
$found{$File::Find::dir . "/"}++;
};
find(\&wanted, @ARGV ? @ARGV : '.');
print join($sep, sort keys %found), $sep if %found;
노트:파일::찾기그리고GetSelect::표준핵심 Perl 모듈이며 Perl에 포함되어 있습니다.
수량::바이트::인간아니요, 별도로 설치해야 합니다(Debian 및 그 파생 제품: sudo apt-get install libnumber-bytes-human-perl
. 다른 배포판에서도 패키지할 수 있습니다. 그렇지 않으면 으로 설치 cpan
).
또는 원시 원시인처럼 및 use Number::Bytes::Human qw(parse_bytes);
행을 제거하고 파일 크기를 바이트 단위로 지정하십시오.$size = parse_bytes($size);
그리고 나서 당신은 "흠... 어쩌면 내가 사용해야 할지도 모르겠다"고 생각합니다Getopt::긴옵션을 Getopt::Std
처리할 수 있는 것보다 디렉터리의 일치 항목 수를 출력하는 옵션을 갖는 것이 유용하고 필요할 수 있습니다.--long
-c
문서그리고...". 어쩌면 그렇게 하기 위해 수정을 시작하다가 "안돼! 이것은미친. 도구를 만드는 것은 재미있지만 그것만으로도 충분합니다. "
미친 사람이 어떤 이름이나 이름도 언급하지 않고 할 수 있는 일에 대한 가상의 예처럼 말입니다. 나는 언제든지 멈출 수 있다. 후원자의 전화번호는 어디에 있나요? 내 생각엔 그 사람들에게 전화해야 할 것 같아.
그런데 다음을 포함하는 디렉토리만 인쇄합니다.정확히일치하는 비디오의 경우 print join ...
행을 다음과 같이 변경할 수 있습니다.
foreach (sort keys %found) {
print "$d\n" if $found{$_} == 1
};
(또는 print "$d$sep" ...
두 번째 버전)
이는 여러 개의 .mp4 파일이 포함된 디렉터리를 인쇄하며 그 중 하나만 <= 10MB입니다. 이를 제외하려면 wanted
해시에 입력되지 않도록 %found
(또는 함수가 완료되기 전에 해시에서 제거되도록 find()
) 서브루틴을 수정해야 합니다 . 다음과 같이 여러 .mp4 파일이 발견된 디렉터리를 추적하기 위해 다른 해시를 사용할 수도 있습니다.
sub wanted {
next unless -f $_ && $File::Find::name =~ /\.mp4\Z/s;
my $d = $File::Find::dir . '/';
$seen{$d}++;
if ($seen{$d} > 1) {
delete $found{$d};
} else {
$found{$d} = 1 if -s $_ <= 10485760;
}
};
그리고 my %found;
그 줄을 다음과 같이 바꾸세요.my (%found, %seen);