나는 다음과 같은 경로를 가지고 있습니다 :
/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
이므로 제외됩니다 .follower
follower
leader1
leader2
2
이제 시간 표준을 추가 -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
find
follower
이름이 로 시작하고 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일 이상 지난 파일(find
s 와 동일-mtime +13
)om
: 수정 시간을 기준으로 정렬(ls -t
최신 파일부터)
디렉터리 수정 시간에 의존하는 것은 위험합니다. 파일이 추가되거나 제거되거나 이름이 바뀌면(또는 디렉토리가 편집되면 ) touch
디렉토리가 수정됩니다 . 이러한 디렉토리에는 번호가 매겨져 있으므로 해당 번호 매기기를 대신 사용하고 싶을 수 있으므로 om
( nOn
숫자는 n
rder O
inverse (capital O
) 을 n
ame로) 바꾸십시오.
변수에 패턴을 포함하려면 및 설정 또는 다른 값 follower<->
으로 바꾸십시오.$~pattern
pattern='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
-printf
stat
-V
sort
-z
sort
tail
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가 아닌 일부 기능, 즉 stat
format 에 의존합니다 %s
date
. 한 손정렬, 확실히...