サイズが10MB未満のmp4ファイルを含むすべてのディレクトリを繰り返し検索しようとしています。
要件は、
- ディレクトリにはmp4ファイルが1つしかありません。
- 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ファイルを考慮していないということです。この質問は2段階に分けることができると思います。
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システムでこれらのファイルの1つだけを含むディレクトリを取得するには、次の手順を実行できます。
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
およびを除いて1つ以上のエントリを含むディレクトリ)を選択します。.
..
e[code]
:成功したファイルを選択してくださいcode
。() {body} arguments
複数のパラメータを持つ匿名関数。- 返された算術評価
{body}
です。(( $# == 1 ))
本物この匿名関数のパラメーター数が1の場合。 $REPLY
内部には、code
検討中のファイル(ここではディレクトリ)へのパスがあります。*.mp4(qualifiers)
: (隠されていない)mp4
ファイルはさらに制限されます。.
:一般ファイルのみ(例:find
's'-type f
)L-10000000
:ファイルサイズは10MBより厳密に小さい。Y2
:2つのファイルを見つけて最適化を停止します。
.
(現在の作業ディレクトリ自体)は考慮されません。これを考慮したい場合**/*
はに変更してください{.,**/*}
。
ここで明確に説明したように、mp4ファイルが1つしか含まれていないディレクトリを探し、そのファイルが通常のファイル(ディレクトリ、シンボリックリンクではありません...)で、サイズが10 MB未満(たとえばを含むディレクトリを除く):5 MBと15 MB mp4ファイル(サイズに関係なく、複数のmp4があるため、10 MB未満のmp4ファイルが1つしかありませんが)、まだ次のようになりますzsh
。
print -rC1 -- **/*(NFe['
() {
(( $# == 1 )) && ()(($#)) $1(N.L-10000000)
} $REPLY/*.mp4(NY2)
'])
GNUfind
と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の通常のファイルを1つ以上含むディレクトリのリストが印刷されます.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" ...
第2版)
これは複数の.mp4ファイルを含むディレクトリを印刷し、そのうちの1つだけ<= 10 MBです。これを除外するには、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);