동일한 접두사를 공유하는 각 파일 그룹에서 디렉터리의 가장 최근 n개 파일을 제외하고 모두 삭제합니다.

동일한 접두사를 공유하는 각 파일 그룹에서 디렉터리의 가장 최근 n개 파일을 제외하고 모두 삭제합니다.

n내 질문은 " 디렉토리의 최신 파일을 제외한 모든 파일 삭제"를 요구하는 일부 이전 질문과 약간 다릅니다 .

각 파일 그룹이 임의의 접두사를 공유하고 각 그룹에 최소한 하나의 파일이 있는 다양한 "그룹"의 파일이 포함된 디렉터리가 있습니다. 사전에 접두사도 모르고 그룹이 몇 개 있는지도 모릅니다.

편집: 사실 제가 아는 파일 이름은 모두 패턴을 따른다는 것입니다 prefix-some_digits-some_digits.tar.bz2. 여기서 중요한 것은 prefix부분이며 prefix각 부분에는 숫자나 대시가 없다고 가정할 수 있습니다.

bash스크립트에서 다음을 수행 하고 싶습니다 .

  1. n지정된 디렉토리를 반복하고 기존의 모든 "그룹"을 식별하고 각 파일 그룹에 대해 해당 그룹의 최신 파일을 제외한 모든 파일을 삭제합니다.

  2. 그룹에 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들어 다음을 추가하세요 . 삭제 코드를 활성화하려면 삭제하세요 .rmrm -v --#

done # | xargs -d '\n' rm --

이것이 효과가 있었다면 이 답변을 수락하고 투표해 주십시오. 감사해요.

답변3

나는 어휘적으로 나열될 때 파일이 접두사별로 그룹화되어 있다고 가정합니다. 이는 다른 그룹의 접미사인 접두어가 있는 그룹이 없음을 의미합니다(예: 및 사이에 foo-1-2-3.tar.bz2나타나지 않음 ) . 이 가정 하에서 우리는 모든 파일을 나열할 수 있으며 접두사 변경(또는 첫 번째 파일)을 감지하면 새 그룹을 갖게 됩니다.foo-1-1.tar.bz2foo-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))])

관련 정보