n
내 질문은 " 디렉토리의 최신 파일을 제외한 모든 파일 삭제"를 요구하는 일부 이전 질문과 약간 다릅니다 .
각 파일 그룹이 임의의 접두사를 공유하고 각 그룹에 최소한 하나의 파일이 있는 다양한 "그룹"의 파일이 포함된 디렉터리가 있습니다. 사전에 접두사도 모르고 그룹이 몇 개 있는지도 모릅니다.
편집: 사실 제가 아는 파일 이름은 모두 패턴을 따른다는 것입니다 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
스크립트는 하나의 매개변수를 허용합니다.N(파일 수)
(:s,-,/,:h)
는 glob 한정자이며, :s
첫 번째 것을 헤더 -
로 /
바꾸고 :h
추출합니다(마지막 슬래시까지의 부분, 이 경우에는 하나만 있기 때문에 첫 번째 슬래시이기도 함) 는 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))])