소개하다

소개하다

나는 다음과 같은 경로를 가지고 있습니다 :

/dir1/dir2/

이 경로에는 다양한(관련되지 않은) 응용 프로그램 Detrius가 포함된 다음 디렉터리가 있습니다.

follower1234  1-Dec-2018
follower3456  2-Dec-2018
follower4567  3-Dec-2018
follower7890  9-Jan-2019
follower8901 10-Jan-2019
leader8765    4-Dec-2018
bystander6789 5-Dec-2018

오늘이 2019년 1월 10일이라고 가정합니다.

followerXXXX, leaderXXXX및 디렉토리가 얼마든지 있을 수 있다고 가정합니다 bystanderXXXX.

내가 원하는 것은 2주가 넘은 followerXXXX최신 디렉토리를 제외한 모든 디렉토리를 삭제하는 것입니다.followerXXX

이제 모든 디렉토리를 삭제할 수 있습니다특정 날짜 이전. 하지만 그건 내 문제가 아닙니다. 두 개의 추가 매개변수를 추가하겠습니다.

이 경우 다음을 제거하고 싶습니다.

follower1234  1-Dec-2018
follower3456  2-Dec-2018
follower4567  3-Dec-2018

하지만

follower7890  9-Jan-2019
follower8901 10-Jan-2019
leader8765    4-Dec-2018
bystander6789 5-Dec-2018

즉, 파일을 삭제하고 싶습니다.

(a) 일치 패턴

(b) 2주 이상

(c)는 패턴과 일치하는 최신 디렉터리가 아닙니다(즉, 마지막 디렉터리를 유지하세요).

내 질문은 다음과 같습니다한 디렉터리에서 2주가 넘은 모든 디렉터리를 삭제하려면 어떻게 해야 합니까(파일 패턴과 일치하는 최신 디렉터리 제외)?

답변1

소개하다

질문이 수정되었습니다.

  • 내 첫 번째 대안(oneliner)은 새 사양과 일치하지 않았지만 충분히 오래된(14일 이상) 디렉터리에서 최신 디렉터리를 저장했습니다.

  • 나는 두 번째 대안(쉘스크립트)을 만들었습니다.

    @ 1970년 1월 1일 00:00 GMT 이후의 초 수(소수 부분 포함).

    seclim정렬된 디렉터리 목록에서 "초 제한"의 타임스탬프를 얻으려면 14일에 해당하는 초 수를 뺍니다.

1. 단선

이전 답변은 깨끗하고 깔끔하지만 최신 디렉토리를 유지하지 않습니다 follower. 다음 명령줄은 이 작업을 수행합니다(공백이 있는 이름을 관리하지만 개행이 있는 이름에는 문제가 발생합니다).

find . -type d -name "follower*" -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r

이 디렉터리 구조를 테스트하려면

$ find -printf "%T+ %p\n"|sort
2019-01-10+13:11:40.6279621810 ./follower1
2019-01-10+13:11:40.6279621810 ./follower1/2/3
2019-01-10+13:11:40.6279621810 ./follower1/2/dirnam with spaces
2019-01-10+13:11:40.6279621810 ./follower1/2/name with spaces
2019-01-10+13:11:56.5968732640 ./follower1/2/file
2019-01-10+13:13:18.3975675510 ./follower2
2019-01-10+13:13:19.4016254340 ./follower3
2019-01-10+13:13:20.4056833250 ./follower4
2019-01-10+13:13:21.4097412230 ./follower5
2019-01-10+13:13:22.4137991260 ./follower6
2019-01-10+13:13:23.4138568040 ./follower7
2019-01-10+13:13:24.4219149500 ./follower8
2019-01-10+13:13:25.4259728780 ./follower9
2019-01-10+13:15:34.4094596830 ./leader1
2019-01-10+13:15:36.8336011960 .
2019-01-10+13:15:36.8336011960 ./leader2
2019-01-10+13:25:03.0751878450 ./follower1/2

이와 같이,

$ find . -type d -name "follower*" -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r
rm -r ./follower1 ./follower2 ./follower3 ./follower4 ./follower5 ./follower6 ./follower7 ./follower8

따라서 최신 디렉터리(이름이 시작하지 않는( 그리고 게임에 없는 디렉터리 )) follower9이므로 제외됩니다 .followerfollowerleader1leader22

이제 시간 표준을 추가 -mtime +14하고 제대로 작동하는지 확인하기 위해 또 다른 "테스트 실행"을 수행합니다. follower실제 디렉토리가 존재하는 곳으로 디렉토리를 변경할 때,

find . -type d -name "follower*" -mtime +14 -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r

echo마지막으로 우리는 원하는 것을 수행하는 명령줄을 제거 하고 갖게 되었습니다.

find . -type d -name "follower*" -mtime +14 -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs rm -r

  • findfollower이름이 로 시작하고 14일 전부터 수정되지 않은 현재 디렉터리의 디렉터리입니다.
  • 인쇄하고 분류한 후head -n -1최신 follower디렉토리를 제외합니다.
  • 타임스탬프를 제거하고 각 디렉터리 이름의 시작과 끝에 큰따옴표를 추가합니다.
  • 마지막 으로 삭제하려는 디렉터리를 삭제하기 위해 결과 xargs가 파이프를 통해 매개변수로 전달됩니다 .rm -r

2. 쉘 스크립트

나는 두 번째 대안(쉘스크립트)을 만들었습니다.

@      seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.

또한 두 가지 옵션이 있습니다.

  • -n시운전
  • -v말 수가 많은

  • OP의 요청에 따라 쉘스크립트를 수정했습니다.모드를 인수로 입력"follower*"와 같은 작은따옴표 내.

  • prune-dirs이제 더 일반적이기 때문에 쉘스크립트 이름을 사용하는 것이 좋습니다 (더 이상 prune-followers디렉토리 정리에만 사용되는 것이 아닙니다 follower*).

수행할 작업을 "확인"하기 위해 처음으로 이 두 가지 옵션을 사용하여 쉘스크립트를 실행하고, 올바르게 보이면 -n쉘스크립트가 삭제할 만큼 오래된 디렉토리를 삭제하는 것이 좋습니다. 그럼 이를 호출 prune-dirs하여 실행 가능하게 만들어 보겠습니다.

#!/bin/bash

# date        sign     comment
# 2019-01-11  sudodus  version 1.1
# 2019-01-11  sudodus  enter the pattern as a parameter
# 2019-01-11  sudodus  add usage
# 2019-01-14  sudodus  version 1.2
# 2019-01-14  sudodus  check if any parameter to the command to be performed

# Usage

usage () {
 echo "Remove directories found via the pattern (older than 'datint')

 Usage:    $0 [options] <pattern>
Examples: $0 'follower*'
          $0 -v -n 'follower*'  # 'verbose' and 'dry run'
The 'single quotes' around the pattern are important to avoid that the shell expands
the wild card (for example the star, '*') before it reaches the shellscript"
 exit
}

# Manage options and parameters

verbose=false
dryrun=false
for i in in "$@"
do
 if [ "$1" == "-v" ]
 then
  verbose=true
  shift
 elif [ "$1" == "-n" ]
 then
  dryrun=true
  shift
 fi
done
if [ $# -eq 1 ]
then
 pattern="$1"
else
 usage
fi

# Command to be performed on the selected directories

cmd () {
 echo rm -r "$@"
}

# Pattern to search for and limit between directories to remove and keep

#pattern='follower*'
datint=14  # days

tmpdir=$(mktemp -d)
tmpfil1="$tmpdir"/fil1
tmpfil2="$tmpdir"/fil2

secint=$((60*60*24*datint))
seclim=$(date '+%s')
seclim=$((seclim - secint))
printf "%s limit-in-seconds\n" $seclim > "$tmpfil1"

if $verbose
then
 echo "----------------- excluding newest match:"
 find . -type d -name "$pattern" -printf "%T@ %p\n" | sort |tail -n1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/'
fi

# exclude the newest match with 'head -n -1'

find . -type d -name "$pattern" -printf "%T@ %p\n" | sort |head -n -1 >> "$tmpfil1"

# put 'limit-in-seconds' in the correct place in the sorted list and remove the timestamps

sort "$tmpfil1" | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' > "$tmpfil2"

if $verbose
then
 echo "----------------- listing matches with 'limit-in-seconds' in the sorted list:"
 cat "$tmpfil2"
 echo "-----------------"
fi

# create 'remove task' for the directories older than 'limit-in-seconds'

params=
while read filnam
do
 if [ "${filnam/limit-in-seconds}" != "$filnam" ]
 then
  break
 else
  params="$params $filnam"
 fi
done < "$tmpfil2"
cmd $params > "$tmpfil1"
cat  "$tmpfil1"

if ! $dryrun && ! test -z "$params"
then
 bash "$tmpfil1"
fi
rm -r $tmpdir
  • follower현재 디렉터리를 하위 디렉터리가 포함된 디렉터리로 변경합니다.
  • 파일 만들기prune-dirs
  • 실행 가능하게 만들어라
  • 두 가지 옵션으로 실행-v -n

    cd directory-with-subdirectories-to-be-pruned/
    nano prune-dirs  # copy and paste into the editor and save the file
    chmod +x prune-dirs
    ./prune-dirs -v -n
    

시험

prune-dirs아래와 같이 다음 하위 디렉터리가 있는 디렉터리에서 테스트했습니다 .find

$ find . -type d -printf "%T+ %p\n"|sort
2018-12-01+02:03:04.0000000000 ./follower1234
2018-12-02+03:04:05.0000000000 ./follower3456
2018-12-03+04:05:06.0000000000 ./follower4567
2018-12-04+05:06:07.0000000000 ./leader8765
2018-12-05+06:07:08.0000000000 ./bystander6789
2018-12-06+07:08:09.0000000000 ./follower with spaces old
2019-01-09+10:11:12.0000000000 ./follower7890
2019-01-10+11:12:13.0000000000 ./follower8901
2019-01-10+13:15:34.4094596830 ./leader1
2019-01-10+13:15:36.8336011960 ./leader2
2019-01-10+14:08:36.2606738580 ./2
2019-01-10+14:08:36.2606738580 ./2/follower with spaces
2019-01-10+17:33:01.7615641290 ./follower with spaces new
2019-01-10+19:47:19.6519169270 .

용법

$ ./prune-dirs
Remove directories found via the pattern (older than 'datint')

 Usage:    ./prune-dirs [options] <pattern>
Examples: ./prune-dirs 'follower*'
          ./prune-dirs -v -n 'follower*'  # 'verbose' and 'dry run'
The 'single quotes' around the pattern are important to avoid that the shell expands
the wild card (for example the star, '*') before it reaches the shellscript

실행 -v -n(상세한 테스트 실행)

$ ./prune-dirs -v -n 'follower*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"./follower1234"
"./follower3456"
"./follower4567"
"./follower with spaces old"
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./2/follower with spaces"
-----------------
rm -r "./follower1234" "./follower3456" "./follower4567" "./follower with spaces old"

보다 일반적인 패턴을 사용한 자세한 연습

$ LANG=C ./prune-dirs -v -n '*er*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"./follower1234"
"./follower3456"
"./follower4567"
"./leader8765"
"./bystander6789"
"./follower with spaces old"
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./leader1"
"./leader2"
"./2/follower with spaces"
-----------------
rm -r "./follower1234" "./follower3456" "./follower4567" "./leader8765" "./bystander6789" "./follower with spaces old"

옵션 없이 실행 (디렉터리 삭제 실제 사례)

$ ./prune-dirs 'follower*'
rm -r "./follower1234" "./follower3456" "./follower4567" "./follower with spaces old"

-v"다시 시도" 실행

$ LANG=C ./prune-dirs -v 'follower*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./2/follower with spaces"
-----------------
rm -r

쉘스크립트는 "초 제한 위" 디렉토리를 나열하지 않으며 rm -r명령줄에 대한 파일을 나열하지 않으므로 작업이 완료됩니다(올바른 결과임). 그러나 며칠 후에 쉘스크립트를 다시 실행하면 일부 새 디렉토리가 "초 제한"을 "초과"하여 삭제될 수 있습니다.

답변2

Rowan의 답변을 보완합니다. 디렉토리 경로별로 포인트를 변경할 수 있습니다.

find . -type d -name follower* -mtime +14 -exec rm -rf {} +;

답변3

그리고 zsh:

(){ n=$#; } follower<->(/)       # count the number of follower<n> dirs

to_remove=(follower<->(/m+13om)) # assumes the dir list is not changed
                                 # since the previous command

(($#to_remove < n)) || to_remove[1]=() # keep the youngest if they're
                                       # all over 2 weeks old



echo rm -rf $to_remove

echo(만족하면 삭제)

  • <->일련의 십진수( <1-20>제한 없는 약어).
  • (){code} args: 인수의 개수가 저장되는 익명 함수입니다 $n.
  • (/omm+13): 글로벌 예선
  • /: 파일 형식만 선택목차( find와 동일 -type d)
  • m+13: 기간이 정확히 13일보다 큰 모든 파일, 즉 14일 이상 지난 파일( finds 와 동일 -mtime +13)
  • om: 수정 시간을 기준으로 정렬( ls -t최신 파일부터)

디렉터리 수정 시간에 의존하는 것은 위험합니다. 파일이 추가되거나 제거되거나 이름이 바뀌면(또는 디렉토리가 편집되면 ) touch디렉토리가 수정됩니다 . 이러한 디렉토리에는 번호가 매겨져 있으므로 해당 번호 매기기를 대신 사용하고 싶을 수 있으므로 om( nOn숫자는 nrder Oinverse (capital O) 을 name로) 바꾸십시오.

변수에 패턴을 포함하려면 및 설정 또는 다른 값 follower<->으로 바꾸십시오.$~patternpattern='follower<->'

답변4

몇 가지 해결 방법:

1. GNU 기반 find:

#!/bin/bash

# The pattern has to be available to subshells
export patt="$1"

find . -maxdepth 1 -type d -name "${patt}*" -mtime +14 \
  -exec sh -c '[ "$(find . -maxdepth 1 -type d -name "${patt}*" -print0 |
    sort -z -V |
    tail -z -n 1 |
    tr -d "\0")" != "$1" ]' sh {} \; \
  -exec sh -c 'echo rm -r "$1"' sh {} \;

스크립트는 다음과 같이 호출됩니다.

./script name_pattern

그대로 테스트 실행을 제공합니다. 실제로 디렉터리가 삭제되도록 echo마지막 작업에서 삭제하세요 .-exec

그것은:

  • 현재 디렉토리에서 수정된 지 14일이 넘고(아래 참고 참조) 각각의 -mtime값이 ;로 시작하는 모든 디렉토리를 찾습니다 ${patt}.
  • -exec발견된 (첫 번째) 디렉토리가 오름차순으로 정렬된 이름 패턴과 일치하는 마지막 디렉토리가 아닌지 확인하세요.버전order( -V)(예: follower100끝에 넣기 follower2), 테스트( [)가 실패 하면 find다음 루프로 점프하고 후속 작업을 수행하지 않습니다.
  • 찾은 디렉터리(두 번째 디렉터리 -exec)를 삭제합니다.

여기서는 이름을 기준으로 사전순으로 디렉토리를 정렬하는 것과 수정 날짜를 기준으로 정렬하는 것 사이에 동등성이 있다고 가정합니다. 만약 당신의최신디렉토리는 이름을 기준으로 정의됩니다.
반대로, 당신의 경우최신디렉토리는 가장 최근에 수정된 디렉토리입니다. -exec ...위 코드의 첫 번째 디렉토리를 다음 디렉토리로 바꿔야 합니다.

  -exec sh -c '[ "$(find . -maxdepth 1 -type d -name "${patt}*" -printf "%T@\n" |
    sed "s/\..*$//" |
    sort -n |
    tail -n 1)" != "$(stat -c "%Y" "$1")" ]' sh {} \; \

내부적으로 find우리는 이름 패턴과 일치하는 모든 디렉토리를 찾고, 에포크 이후의 수정 시간 목록(초 단위)을 인쇄하고, 소수 부분을 제거하고, 정렬하고, 마지막 부분을 가져와 외부적으로 현재 수정 시간과 같지 않은지 확인합니다 find.

이 필터를 사용할 때 일치하는 모든 디렉터리가 14일보다 오래되었고 수정 시간이 정확히 동일한 경우 디렉터리가 삭제되지 않습니다.


노트:

-maxdepth 1현재 디렉터리( )의 내용으로 검색을 제한해야 한다는 엄격한 요구 사항은 없습니다.

예를 들어 스크립트 시작 부분에 추가하는 sort방법을 알려주고 싶을 수도 있습니다 (참조).export LC_ALL=C"'LC_ALL=C'는 무엇을 합니까?"에 대한 답변입니다.현지화 설정에 따라 발생할 수 있는 정렬 문제에 대해 설명합니다.

을 사용하면 -mtime +14수정 시간이 기술적으로 14*24시간보다 오래되었더라도 14~15일 전에 수정된 파일을 건너뜁니다.지금( man find자세한 내용을 참조하십시오. 자세한 내용은 설명을 참조하십시오 -atime n.)

이름에 공백, 줄 바꿈, 특이하고 인쇄할 수 없는 문자가 포함되어 있어도 작동합니다.

호환성: 단점은 이식성이 없다는 것입니다. 여기서 사용된 일부 기능, 특히 , 및 , find명령, 옵션 및 옵션(자세한 내용은 잊어버렸을 것입니다)은 POSIX에 지정되어 있지 않습니다.-maxdepth-print0-printfstat-Vsort-zsorttail

2. 쉘 기능 기반

#!/bin/sh

patt="$1"                 # The name pattern
test -z "$patt" && exit   # Caution: pattern must not be empty

days=14     # How old has to be a directory to get deleted, in days?
last=       # The youngest directory

dirs=( "$patt"* )     # Array of files matched by name (note, here we
                      # have everything that matches, not just dirs)
now="$(date +'%s')"   # Now in seconds since Epoch

threshold="$(( "$now" - ( "$days" * 24 * 60 *60 ) ))"
                      # Dirs older than this date (in seconds since
                      # Epoch) are candidates for deletion

# We find the youngest directory in the array
#
for i in "${!dirs[@]}"; do
  if  [ -z "$last" ] ||
    ( [ -d "${dirs[$i]}" ] &&
      [ "$(stat -c '%Y' -- "${dirs[$i]}")" -gt "$(stat -c '%Y' -- "$last")" ] ); then
    last="${dirs[$i]}"
  fi
done

# We delete all the directories in the array that are
# not the youngest one AND are older that the thrashold
#
for i in "${!dirs[@]}"; do
  if  [ -d "${dirs[$i]}" ] &&
      [ "${dirs[$i]}" != "$last" ] &&
      [ "$(stat -c '%Y' -- "${dirs[$i]}")" -lt "$threshold" ]; then
    echo rm -rf -- "${dirs[$i]}"
  fi
done

스크립트는 다음과 같이 호출되어야 합니다.

./script name_pattern

echo다시 말하지만, 제거할 때까지 시험 실행이 제공됩니다 echo rm -rf -- "${dirs[$i]}".

그것은:

  • 이름 패턴과 일치하는 현재 디렉터리의 모든 파일 이름으로 배열을 채웁니다.
  • 배열의 최신 디렉터리를 확인합니다.
  • 1) 14일보다 오래되었고 2) 최신 디렉터리가 아닌 배열의 모든 디렉터리를 삭제합니다.

노트:

14일보다 오래된 디렉터리를 대상으로 합니다.지금( 와는 다름 find). 따라서 두 솔루션은 엄격하게 동일하지 않습니다.
또한 일치하는 모든 디렉터리가 임계값보다 오래되고 수정 시간이 동일한 경우 무작위로 선택된 디렉터리 중 하나만 제외하고 모두 삭제됩니다.

줄 바꿈 및 인쇄할 수 없는 문자를 포함하여 특이한 문자가 포함된 이름은 괜찮습니다.

호환성: 이 솔루션도 POSIX가 아닌 일부 기능, 즉 statformat 에 의존합니다 %s date. 한 손정렬, 확실히...

관련 정보