すべてのディレクトリで10MB未満のmp4ファイルを繰り返し検索します。

すべてのディレクトリで10MB未満のmp4ファイルを繰り返し検索します。

サイズが10MB未満のmp4ファイルを含むすべてのディレクトリを繰り返し検索しようとしています。

要件は、

  1. ディレクトリにはmp4ファイルが1つしかありません。
  2. 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段階に分けることができると思います。

  1. mp4ファイルを含むすべてのディレクトリを探します。これは上記のコマンドで行われます。

  2. ファイルが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'

どこ

  • findhこれらのファイルのead(ディレクトリ名)をNULで区切って印刷します(現在のロケールで有効なテキストでなくても、LC_ALL=C他の方法で終わるすべてのファイル名が報告されます)。.mp4
  • sortこれをソートしますuniqLC_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... Fglob修飾子でさらにe限定されます。
  • Nnullglob不一致の場合はエラーを返すのではなく、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の場合、ディレクトリを再帰的に検索するためのまともなライブラリを持つ言語で必要な操作を実行するカスタムツールを直接作成してこれを行うには、シェルコマンドラインエディタの代わりにまともなエディタを使用する方が簡単です。grepuniq

したがって、次のような別の小さなバリエーションを作成します。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);

関連情報