web-dev-qa-db-ja.com

最後の(最新の)20を除くフォルダー内のすべてのファイルを削除します

異なるディレクトリからすべてのファイルを削除し、最新のファイルのみを20ファイル保持したいと思います。これはこれを行うための正しいコマンドですか?

ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f
4
Ali

あなたのコマンドには多くの欠点があります。たとえば、lsの出力の処理は、一種のカテキズムです。 この記事 これを行わない理由を参照してください。

このアプローチでは、noGNU拡張機能を使用し、bash、zsh、ksh93、dash、およびashでテストされています。

for f in * .*; do 
  [ -f "$f" ] && printf "%s " $(Perl -e 'print((stat("$ARGV[0]"))[9])' -- "$f") && \
  printf "%s\0" "$f";
done | tr '\0\n' '\n\0' | sort -k1nr | \
sed "1,20d;s/'/'\"'\"'/g;s/[^ ]* \(.*\)/rm -f -- '\1';/" | tr '\0' '\n' | sh

これは、私が作成できた最も奇妙なファイル名です。これでも動作します。

touch -- '--a'"'"'b"c
d$e\nf\r!g;h^i`j(k)l*m%n=o?p.txt'

または、「ジャンプアウト」しようとするファイル名:

touch '\'"'"'echo test '"'"'\'
3
chaos

zshおよび glob-qualifiers の場合:

print -rl -- *(D.Om[1,-21])

最後の(最後に変更された)20を除くすべての通常ファイルをリストします。
Dは隠しファイルを選択し、.は通常のファイルのみを選択します。Ommtimeで逆ソートすることを意味します(とても古いものから)と[1,-21]最初から21番目から最後までを選択します。
結果に満足したら、print -rl with rm

rm -- *(D.Om[1,-21])

膨大な数のファイルがある場合は、引数リストが長すぎるのを避けるためにzargsを使用する必要があるかもしれません。

autoload zargs
zargs ./*(D.Om[1,-21]) -- rm
3
don_crissti
ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f

lsの出力にはファイルの名前のみが含まれ、フルパスは含まれないため、機能しません。名前が.で始まるファイルもスキップします。

cd /mnt/dwh/ftp/dwh && ls -At1 | tail -n +21 | xargs rm -f --

それでも解決しますが、空白、改行、アポストロフィ、バックスラッシュ、二重引用符を含むファイル名(および、ロケールで有効な文字を形成しないバイトを含むファイル名)にはまだ問題があります。

(export LC_ALL=C
 cd /mnt/dwh/ftp/dwh && ls -At1 |
   tail -n +21 |
   sed 's/"/\\"/g;s/.*/"&"/' |
   xargs rm -f --
)

それらのほとんどは修正されますが、改行文字を含むファイル名にはまだ問題があります。問題は、ls -At1の出力が後処理できないことです。

ls -Atd1 ./.* ./*の出力は後処理できますが(./プレフィックスは、各ファイル名が出力のどこから始まるかを示しますが、簡単ではありません)、引数の数が上限に達するリスクがあります。そのようにファイル名のリストをlsに渡すことによってコマンドに渡されます。

最善の方法は グロブでそのソートを実行できるシェルを使用する 、またはGNUシステムでのみ作業する必要がある場合は、GNU拡張機能に依存することです。

 cd /mnt/dwh/ftp/dwh &&
   find . ! -name . -Prune ! -type d -printf '%T@\t%p\0' |
     tr '\0\n' '\n\0' |
     sort -rn |
     tail -n +21 |
     cut -f 2- |
     tr '\0\n' '\n\0' |
     xargs -r0 rm -f

ここでは、タイプdirectoryのファイルを除外しています。これは番号付けに影響しますが、おそらくあなたが望むとは思わないので、あなたが望むものに近いでしょう。ディレクトリを削除します。ディレクトリを削除する場合は、-type dを削除し、-rオプションをrmに追加します。

または:

eval "set -- $(COLUMNS=4294967295 ls -Atx --quoting-style=Shell-always)"
if [ "$#" -gt 20 ]; then
  shift 20
  printf '%s\0' "$@" | xargs -r0 rm -f --
fi

または、PerlpythonRuby ..などのジェネリックプログラミング言語を使用します。

3