![同じプレフィックスを共有する各ファイルグループから、ディレクトリ内の最新のn個のファイルを除いてすべて削除します。](https://linux33.com/image/76567/%E5%90%8C%E3%81%98%E3%83%97%E3%83%AC%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E5%85%B1%E6%9C%89%E3%81%99%E3%82%8B%E5%90%84%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%81%8B%E3%82%89%E3%80%81%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E5%86%85%E3%81%AE%E6%9C%80%E6%96%B0%E3%81%AEn%E5%80%8B%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E9%99%A4%E3%81%84%E3%81%A6%E3%81%99%E3%81%B9%E3%81%A6%E5%89%8A%E9%99%A4%E3%81%97%E3%81%BE%E3%81%99%E3%80%82.png)
n
私の質問は、「ディレクトリ内の最新のファイルを除くすべてのファイルを削除する」を必要とする以前の質問とは少し異なります。
各ファイルグループは任意のプレフィックスを共有し、各グループには少なくとも1つのファイルを含むさまざまな「グループ」のファイルを含むディレクトリがあります。事前にプレフィックスも知らず、グループが何個あるのかもわかりません。
編集:実際に私が知っているファイル名はすべてパターンに従うことですprefix-some_digits-some_digits.tar.bz2
。ここで重要なのはprefix
部分であり、prefix
各部分に数字やダッシュがないと仮定できます。
bash
スクリプトで次のことをしたいと思います。
n
指定されたディレクトリを繰り返し、既存のすべての「グループ」を識別し、各ファイルグループについて、そのグループの最新ファイルを除くすべてのファイルを削除します。グループに
n
グループより少ない数のファイルがある場合、グループに対して何もしません。つまり、グループ内のファイルは削除されません。
上記の作業を実行する強力で安全な方法は何ですかbash
?このコマンドを段階的に説明できますか?
答え1
スクリプト:
#!/bin/bash
# Get Prefixes
PREFIXES=$(ls | grep -Po '^(.*)(?!HT\d{4})-(.*)-(.*).tar.bz2$' | awk -F'-' '{print $1}' | uniq)
if [ -z "$1" ]; then
echo need a number of keep files.
exit 1
else
NUMKEEP=$1
fi
for PREFIX in ${PREFIXES}; do
ALL_FILES=$(ls -t ${PREFIX}*)
if [ $(echo ${ALL_FILES} | wc -w) -lt $NUMKEEP ]; then
echo Not enough files to be kept. Quit.
continue
fi
KEEP=$(ls -t ${PREFIX}* | head -n${NUMKEEP})
for file in $ALL_FILES ; do
if [[ "$KEEP" =~ "$file" ]]; then
echo keeping $file
else
echo RM $file
fi
done
done
説明する:
- プレフィックスの計算:
something-something-something.tar.bz2
正規表現に従うすべてのファイルを見つけ、最初の部分だけを最初のダッシュに切り取り、一意にします。- 結果は標準化されたリストです。
PREFIXES
- すべて繰り返します
PREFIXES
。 ALL_FILES
次に計算PREFIX
- 数字が
ALL_FILES
保持するファイルの数より少ないことを確認 - > trueの場合は、何も削除せずにここで停止できます。 KEEP
最近のNUMKEEP
ファイル数の計算- 繰り返して、指定されたファイルがファイルリストに
ALL_FILES
ないことを確認してください。KEEP
その場合は削除してください。
実行時の結果の例:
$ ./remove-old.sh 2
keeping bar-01-01.tar.bz2
keeping bar-01-02.tar.bz2
RM bar-01-03.tar.bz2
RM bar-01-04.tar.bz2
RM bar-01-05.tar.bz2
RM bar-01-06.tar.bz2
keeping foo-01-06.tar.bz2
keeping foo-01-05.tar.bz2
RM foo-01-04.tar.bz2
RM foo-01-03.tar.bz2
RM foo-01-02.tar.bz2
$ ./remove-old.sh 8
Not enough files to be kept. Quit.
Not enough files to be kept. Quit.
答え2
要求されたように、この答えは速くて汚れた答えよりも「頑丈で安全な」方向に傾いています。
sh
移植性:この回答は、、、、、、、およびをfind
含むsed
すべてのシステムsort
で機能します。ls
grep
xargs
rm
スクリプトは大きなディレクトリでブロックされてはいけません。シェルファイル名の拡張を行わないでください(ファイルが多すぎるとブロックされる可能性がありますが、これは膨大な数です)。
この回答では、プレフィックスにダッシュ(-
)が含まれていないと仮定します。
意図的に、このスクリプトは削除されるファイルのみをリストすることに注意してください。スクリプトでコメント化されたループの出力をパイピングしてwhile
ファイルを削除できますxargs -d '/n' rm
。これにより、コードの削除を有効にする前にスクリプトを簡単にテストできます。
#!/bin/sh -e
NUM_TO_KEEP=$(( 0 + ${1:-64000} )) || exit 1
find . -maxdepth 1 -regex '[^-][^-]*-[0-9][0-9]*-[0-9][0-9]*.tar.bz2' |
sed 's/-.*//; s,^\./,,' |
sort -u |
while read prefix
do
ls -t | grep "^$prefix-.*-.*\.tar\.bz2$" | sed "1,$NUM_TO_KEEP d"
done # | xargs -d '\n' rm --
Nパラメーター(保管するファイル数)のデフォルト値は64000(つまり、すべてのファイルを保持)です。
コメント付きコード
コマンドライン引数を取得し、さらに整数を確認します。引数が指定されていない場合、デフォルトは64000(実際にはすべて)です。
NUM_TO_KEEP=$(( 0 + ${1:-64000} )) || exit 1
現在のディレクトリで、ファイル名パターンと一致するすべてのファイルを見つけます。
find . -maxdepth 1 -regex '[^-][^-]*-[0-9][0-9]*-[0-9][0-9]*.tar.bz2' |
プレフィックスのインポート:プレフィックスの後のすべてのエントリを削除し、先行する「./」を削除します。
sed 's/-.*//; s,^\./,,' |
プレフィックスの並べ替えと重複排除( -u
--unique):
sort -u |
各プレフィックスとプロセスをお読みください。
while read prefix
do
時間ごとに並べ替えられたディレクトリ内のすべてのファイルを一覧表示し、現在のプレフィックスがあるファイルを選択して、保持したいファイルを除くすべての行を削除します。
ls -t | grep "^$prefix-.*-.*\.tar\.bz2$" | sed "1,$NUM_TO_KEEP d"
ファイルを削除するコードをコメントアウトしてテストします。コマンドラインの長さやファイル名の空白(存在する場合)の問題を回避するには、xargsを使用してください。スクリプトにログを生成させるには、たとえば、-v
次のように追加します。削除コードを有効にするには、削除してください。rm
rm -v --
#
done # | xargs -d '\n' rm --
これがうまくいったら、この回答を受け入れて投票してください。ありがとうございます。
答え3
私は、語彙的にリストされているときにファイルがプレフィックスごとにグループ化されていると仮定します。これは、他のグループの接尾辞であるプレフィックスを持つグループがないことを意味します(たとえば、および間にはfoo-1-2-3.tar.bz2
表示されません)。この仮定の下では、すべてのファイルを一覧表示でき、プレフィックスの変更(または最初のファイル)を検出すると新しいグループが作成されます。foo-1-1.tar.bz2
foo-1-2.tar.bz2
#!/bin/bash
n=$1; shift # number of files to keep in each group
shopt extglob
previous_prefix=-
for x in *-+([0-9])-+([0-9]).tar.bz2; do
# Step 1: skip the file if its prefix has already been processed
this_prefix=${x%-+([0-9])-+([0-9]).tar.bz2}
if [[ "$this_prefix" == "$previous_prefix" ]]; then
continue
fi
previous_prefix=$this_prefix
# Step 2: process all the files with the current prefix
keep_latest "$n" "$this_prefix"-+([0-9])-+([0-9]).tar.bz2
done
今私たちが議論する内容は明示的なリストで最も古いファイルを確認する。
ファイル名に改行文字やls
リテラル以外の文字が含まれていないと仮定すると、次のことができますls
。
keep_latest () (
n=$1; shift
if [ "$#" -le "$n" ]; then return; fi
unset IFS; set -f
set -- $(ls -t)
shift "$n"
rm -- "$@"
)
答え4
私はこれがタグ付けされていることを知っていますが、bash
簡単になると思いましたzsh
。
#!/usr/bin/env zsh
N=$(($1 + 1)) # calculate Nth to last
typeset -U prefixes # declare array with unique elements
prefixes=(*.tar.bz2(:s,-,/,:h)) # save prefixes in the array
for p in $prefixes # for each prefix
do
arr=(${p}*.tar.bz2) # save filenames starting with prefix in arr
if [[ ${#arr} -gt $1 ]] # if number of elements is greather than $1
then
print -rl -- ${p}*.tar.bz2(Om[1,-$N]) # print all filenames but the most recent N
fi
done
スクリプトは1つのパラメータを受け入れます。N(ファイル数)
(:s,-,/,:h)
はglob修飾子であり、最初のものを:s
ヘッダー-
に/
置き換えて:h
抽出します(最後のスラッシュまでの部分、この場合は1つしかないので最初のスラッシュでもあります)はTakeの
(Om[1,-$N])
glob修飾子です。Om
最も古いファイルを選択し、[1,-$N]
最初からN番目から最後まで選択します。
結果がうまくいけば、実際にファイルを削除するには、次のようにprint -rl
置き換えます。rm
#!/usr/bin/env zsh
typeset -U prefixes
prefixes=(*.tar.bz2(:s,-,/,:h))
for p in $prefixes
arr=(${p}*.tar.bz2) && [[ ${#arr} -gt $1 ]] && rm -- ${p}*.tar.bz2(Om[1,-$(($1+1))])