x行未満のファイルをすばやく削除する方法


10

bashで、x行以下のディレクトリにあるすべてのファイルを削除するための、速くて複雑すぎない方法は何ですか?

回答:


10

理解するのが非常に簡単なPOSIXソリューションを次に示します。

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;

以下のようにステファンの答え、削除echo削除されるものとするときに幸せを。


説明は、Unix / Linuxにまったく新しい人のために書かれました。

ドット.は現在のディレクトリを表します。 find.でファイルとディレクトリを再帰的に検索し、それらを使用して処理を実行できます。

-type一つであるfind予備選挙。これは、(内で.)再帰的に検出されるファイルとディレクトリごとに実行されるテストであり、行の残りのプライマリは、これが「true」になる場合にのみ評価されます。

この特定のケースでは、ディレクトリなどではなく通常のファイル(ブロックデバイスなど)を扱っている場合にのみ続行します。


-exec(の主はfind)外部コマンドを呼び出し、外部コマンドの終了が正常に(「0」の終了状態)場合のみ、次のプライマリに移行します。{}で「とみなさ」されているファイル名に置き換えられているfindコマンド。したがって、最初の-exec呼び出しは、ファイルごとに順番に実行される次のシェルコマンドと同等です。

awk -v x=10 'NR==x{exit 1}' ./somefilename

Awkはそれ自体が言語全体であり、 CSVなどの区切られたテキストファイルを処理するため設計されています。Awkの条件文とコマンド(一重引用符で囲まれ、文字で始まるNR)は、テキストファイルのすべての行に対して実行されます。(暗黙のループ。)

Awkを完全に学習するには、Grymoireチュートリアルを強くお勧めますが、上記のコマンドで使用されるAwkの機能について説明します。


-vAwkへのフラグにより​​、Awkコマンドが実行される前に(ファイルの各行に対して)Awk変数を(1回だけ)設定できます。この場合、に設定x10ます。


NR特別なawkの変数を参照している「Nの現在のアンバーのR ECORD。」つまり、ループの特定のパスで見ている行番号です。

(通常ではありませんが、を設定することにより、改行文字のデフォルトとは異なる " R ecord S eparator" を使用することできます。 ここでは、レコード区切り記号を使用した例を示します。RS


Awkスクリプトは一般に、アクション(中括弧内)と組み合わせた条件(中括弧外)で構成されます。複合条件と複合アクションがあり、デフォルト条件(true)とデフォルトアクション(印刷)がありますが、それらを気にしないでください。

ここでの条件は、「これは10行目ですか」です。この場合、ゼロ以外の終了ステータスで終了します。これは、シェルスクリプトでは「コマンドの終了に失敗した」ことを意味します。

したがって、このAwkコマンドが正常に終了する唯一の方法は、10行目に達する前にファイルの終わりに達した場合です。

したがって、Awkスクリプトが正常に終了した場合、10行未満のファイルがあることを意味します。


次の-exec呼び出し(を削除した場合echo)は、次findのコマンドを実行して、各ファイルを削除します(これは、のプライマリの評価でこれまでに取得したものです)。

rm -f ./somefilename

5

述語findをサポートする実装を想定し-readableます(それがサポートされてfindいない場合は、削除するだけで、読み取り不可能なファイルのエラーメッセージが表示されるか、で置き換えます-exec test -r {} \;)。

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +

echo幸せなら削除します。

これは、すべてのファイルのすべての行を数えるという点では特に効率的ではありませんが、1行x目で停止するだけで、各ファイルに対してwc1 つ(場合によっては1 つrm)のコマンドを実行します。

GNU awkを使用すると、次のようにしてより効率的にすることができます。

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f

(もう一度、echo幸せなときに削除します)。

と同じperl

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +

幸せなら交換printunlinkてください。


1.最後shは何ですか?2. wc -l < "$file"より速いですwc -l "$file"か?3. sh $xは、呼び出し側のBashシェルで定義されているの値をどのようにして知るのですか?

3
@tomas、最後sh$0、たとえば、エラーメッセージに使用するために、そのインラインスクリプトのに入るものです。wc -l "$file"ここでは不要なファイル名が出力されwc、ファイルを開くことができない場合でも実行されます。()に$xエクスポートされ、それ自体がに渡します。findx=10 find...sh
ステファンChazelas

ありがとう!しかし、OSXでこのエラーが発生するということは、私のBashのバージョンが-readableフラグをサポートしていないことを意味していると思いますか?find: -readable: unknown primary or operator
durrrutti 16

1
@durrrutti、それだけではありませんbashbash単なるコマンドラインインタープリターですが、find実装の一部です。-readableGNU拡張機能であり、OS / Xでは使用できませんfind。読み取り可能なファイルに限定するためにのみ使用されます(読み取り不可能なファイルの行数を取得することはできません)。最初の1つは省略できwcます。読み取り不可能なファイルのファイルを開くと、エラーメッセージが表示されます。
ステファンChazelas

@StéphaneChazelas、この答えはとてもトリッキーです。私は疑問に思っています。私の答えでエッジケースを見逃しましたか?:)
ワイルドカード

2

完全を期すために、AWKのほかに、GNU sedを使用して同じ結果を達成することもできます。

find . -type f -exec sed 11q1 '{}' ';' -exec echo rm -f '{}' ';'

これにより、コマンドラインが少し簡潔になります。

説明

11 - is the address, i.e. "the eleventh line"
q - is for _q_uit (abort the execution)
1 - is the exit code parameter for q (GNU sed extension) 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.