複数のレベルで指定されたファイル/ディレクトリを除くすべてのファイル/ディレクトリを削除する

複数のレベルで指定されたファイル/ディレクトリを除くすべてのファイル/ディレクトリを削除する

1行に以下を行うことは可能ですか?

shopt -s extglob

rm -r !(folder1|file1|folder2)
rm -r folder2/!(subfolder)
rm -r folder2/subfolder/!(onefile)

例えばこんなこと?

rm -r !(folder1|file1|folder2/subfolder/onefile)

答え1

-pathツールの(マイナス)テストを使用できますfind。ツールがサポートしている場合は-delete比較的簡単です。

find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -delete

onefile存在する場合はエラーが報告さsubfolderれます。これにより生き残ることができますが、終了状態は維持されません。folder2cannot delete: Directory not emptyfind0

findサポートしている場合は、-emptyディレクトリの前にディレクトリが空であることをテストできます-delete。一般的に言えば、ディレクトリ以外の項目が空であることをテストしたくありません。これによりコマンドが複雑になりますが、0すべてが計画通りに進むとコマンドが返される可能性があります。このように:

find . ! -path ./folder1 \
       ! -path './folder1/*' \
       ! -path ./file1 \
       ! -path ./folder2/subfolder/onefile \
       \( ! -type d -o -empty \) \
       -delete

find支援できる資金が十分で、-deleteおそらく-emptyそうです-regex。私のDebianのGNUはfind次のことができます:

find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2/subfolder/onefile' -delete

メモ:

  • onefilefindこれは削除subfolderまたは存在を防ぎますfolder2。これらのディレクトリが存在するかどうかに関係なく、これらのディレクトリを保存するには、より多くのパターン(以下を参照)またはより複雑な正規表現が必要ですonefile。より複雑な正規表現を使用した変形:

    find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2\(/subfolder\(/onefile\)?\)?' -delete
    

    ボーナスとして、この改善は「ゼロ以外の終了状態」の問題を解決します。

  • -pathglobなどのパターンマッチング表記を使用します(?単一文字に一致し、*ゼロ文字以上に一致)。-regex正規表現を使用します(.単一文字に一致し、.*ゼロ文字以上に一致)。

  • -path-regexファイルのフルパスと一致します。効果的なパターンを設計するには、findどのパスが使用されるかを知る必要があります。ヒント:

    • find始点以外のパスには、末尾のスラッシュを使用しない義務があります。だから()とファイル-path./folder1別々のモードを使用します./folder1/*
    • 一方、始点は指定されたとおりに使用されます。始点がある場合、すべてのパスは自分のパスを除いて.開始されます。すると になります。始点がある場合、すべてのパスが始まります。./..././
  • 私のテストでは-delete自動的に処理され.削除されず、エラーが発生しないため、除外する必要はありません.。一方、エラーが発生します./。この動作がどのくらい信頼できるかわかりません。疑わしい場合は、明示的に除外してください.。特に意味のある終了状態を探している場合にはさらにそうです。

  • 正規表現型はほとんどありません。。たとえば、-regextype posix-extendedbeforeを使用できます! -regex私が思いついた最も簡単なコマンド(携帯用ではありませんが):

    find . -regextype posix-extended \
         ! -regex './folder1(/.*)?|./file1|./folder2(/subfolder(/onefile)?)?' \
           -delete
    

    .意図的に(リテラルドット)の代わりに(ワイルドカード)を\.先頭点として使用しました.。開始点があるため、.すべてのパスは.確実に始まるため、ワイルドカードは以外のものと一致しません.\.正式な精度を好む場合は使用してください。

  • あまり削除しない場合は、コマンドを「テスト実行」できます。-deleteに変更する-printと、削除したい項目が表示されます。これは次のように修正された最初のコマンドです。

    find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -print
    

    または、以前のテストをすべて無効にする全体的に(これは、何を変更すべきかを知らない限り、個別に否定するものとは異なります。比較してください。これ到着これ)変更して何が生き残るかを確認-deleteします-print

    find . ! \( ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile \) -print
    
  • 引用符を使用しないと./folder1/*エラーが発生します。シェルは拡張を試みます*


-pathはいPOSIXの要件しかし、-deleteそして-regexいいえ-empty。時には-exec rm {} +代わりに使用することもできますが、必要なディレクトリは削除されません-delete。はい、ディレクトリを削除することもできますが、全体のポイントも削除され無効になります。rmrmdirrm -rrm -r ./folder2./folder2/subfolder/onefile

-delete私たちの基本的なアプローチは、次のことなく(いくつかの改善を適用することによって)実装できます。

find . -depth \
     ! -path  . \
     ! -path  ./folder1 \
     ! -path './folder1/*' \
     ! -path  ./file1 \
     ! -path  ./folder2 \
     ! -path  ./folder2/subfolder \
     ! -path  ./folder2/subfolder/onefile \
       \( \( ! -type d -exec rm {} + \) -o \( -type d -exec rmdir {} + \) \)

./folder2を明示的に除外することで、コマンドが./folder2/subfolder意味のある終了状態を返すようにし、これらのディレクトリがonefile存在しなくても保存します。

onefile存在しないディレクトリを削除するには、追加のパターンを使用しないでください。研究--ignore-fail-on-non-emptyオプションrmdir。 POSIXにはこのオプションは必要ありません。

私は最後の命令が厳しいとは言わないでしょう。私はそれが-regex良いです。

答え2

これらの潜在的に破壊的な作業の場合は、手動で慎重に実行します。フルパスを含むすべてのファイルのリストを取得し、残る必要があるファイルを編集するか(またはお気に入りのテキストエディタを使用して欠落しているファイルがgrep -vあるかどうかを確認します)。リストをフィードします(rmリストが大きい場合は使用する必要があるかもしれません)。xargs(1)スペースやその他の珍しい文字を含むファイル名に注意してください。

関連情報