名前がファイルリストの行と一致しないディレクトリ内のすべてのファイルを削除します。

名前がファイルリストの行と一致しないディレクトリ内のすべてのファイルを削除します。

1000を超えるファイルを含むディレクトリがあります。テキストファイルには、1行に1つずつ約50のファイル名があります。ファイル名がリストのエントリと一致しないディレクトリ内のすべてのファイルを削除したいと思います。最良のアプローチは何ですか?シェルスクリプトを起動しましたが、ファイル名がリストにあることを確認する正しいコマンドが見つかりませんでした。ありがとうございます。

答え1

ファイルを削除する方法を尋ねる質問は非常に注意して行う必要があることを知っています。私の最初の答えはあまりにも早すぎて、ファイルリストがegrepで使用するのに間違った形式になる可能性があることを考慮していませんでした。このリスクを減らすために回答を編集しました。

これは、名前にスペースが含まれていないファイルに対して機能します。

まず、ファイルのリストを再作成して、正しいファイル名と一致することを確認します。

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

rm コマンドのビルド

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

rmスクリプトが機能していることを確認してください(「vim」または「less」を使用して実行できます)。
次に、次のようにします。

sh -x rmscript

ファイル名にスペースが含まれている場合("ファイル名にスペースが含まれていると機能しません):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

もちろん、ファイルのリストが同じディレクトリにあるべきではありません!

編集する:

Nathanのファイルリストには、ディレクトリ内のすべてのファイルと一致する名前が含まれています(たとえば、「html」は「bob.html」と一致します)。したがって、egrep -vfすべてのストリームが吸収されるため、何も削除されません。各ファイル名の周りに「^」と「$」を入れるコマンドを追加しました。幸いなことに、Nathanのファイルリストは正確でした。 CR-LF 行終了または追加の空白を使用する DOS 形式の場合、egrep はファイルを保持せず、すべてのファイルが削除されます。

答え2

次のパラメータを事前設定しますfind

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

このecho部品を使用して何ができるかを確認してください。部品のみを取り外すechoとすぐに使えます。

アップデート:デモ:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"

答え3

そしてzsh

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

filelist配列の行を読み、次を使用します。グローバル修飾子/e文字列グローバル/選択配列にないファイル名のみ:.通常のファイルのみを選択し(Dリストにドットファイルが含まれている場合は追加)、式^e_'expression'_がfalseを返す選択ファイルのみを無効にします。つまり、対応する名前($REPLY配列の要素ではありません。
結果がうまくいけば、実際にファイルを削除するには、次のprint -rlように置き換えます。rm

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

ファイルを再帰的に選択して削除するには、*/**glob を使用します。${REPLY:t}グローバル修飾子:

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)

答え4

次のスクリプトを実行してください。

  1. 最初はディレクトリ内のすべてのファイルを見つけ、出力を別のファイルに保存しますall_files
  2. 以下のファイルのリストを含むファイルがあります。いいえ削除( not_to_be_deleted_files)。
  3. not_to_be_deleted_files両方のファイルが必要なので、最後にファイル名を追加しました。files_to_be_deletednot_to_be_deleted_files
  4. これで、Linuxコマンドを使用して削除するファイルを見つけて、 join出力をfiles_to_be_deleted ファイルにリダイレクトしています。
  5. 最後のwhileループはその中のすべてのファイル名を読み取り、 files_to_be_deletedそのファイル名に記載されているファイルを削除します。

スクリプトは次のとおりです。

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

ポリスチレン:おそらく、スクリプトとして保存して実行したい場合は、を使用してスクリプト名を追加できますecho scriptname >> not_to_be_deleted_files

必須ではありませんが、後で後悔することはないので、そうすることを好みます。私は小さなファイルセットでテストしましたが、私のシステムで動作します。ただし、確実に確認するには、testまずディレクトリで試してから元のディレクトリのファイルを削除してください。

関連情報