답변:
기존 답변의 문제점 :
rm
인용되지 않은 명령 대체 ( rm `...`
)를 직접 호출하는 솔루션의 경우 의도하지 않은 글로브가 발생할 위험이 추가됩니다.rm
하므로 디렉토리에 적용 하는 데 실패 함).wnoise의 답변 은 이러한 문제를 해결하지만 해결책은 GNU에 따라 다릅니다 (매우 복잡합니다).
한 가지주의 사항 만 제공 되는 실용적인 POSIX 호환 솔루션 은 다음과 같습니다. 줄 바꿈 이 포함 된 파일 이름을 처리 할 수 없습니다. 대부분의 사람들이 실제로 우려하는 것은 아닙니다.
기록을 위해 다음은 일반적으로 ls
출력 을 구문 분석하는 것이 좋지 않은 이유에 대한 설명입니다 . http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
각 파일 이름 마다 한 번씩 호출해야 하므로 위의 내용은 비효율적 입니다.
플랫폼 에서이 문제를 해결할 수 있습니다.xargs
rm
xargs
당신이있는 경우 GNU xargs
사용 -d '\n'
차종은, xargs
각각의 입력 라인 별도의 인수 고려를 아직 명령 줄에 맞는 많은 인수로 전달 번 :
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
( --no-run-if-empty
)는 rm
입력이 없으면 호출되지 않도록합니다.
당신이있는 경우 BSD xargs
(에 포함 맥 OS를 ), 당신이 사용할 수있는 -0
처리 NUL
로 처음 번역의 뉴 라인 후, 단락 지어진 입력 NUL
( 0x0
) 문자를, 또한 (일반적으로) 통과하는 모든 파일 이름. 한 번에 (것 또한 GNU와 작업 xargs
) :
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
설명:
ls -tp
파일 시스템 항목의 이름을 최근에 수정 한 파일 순서대로 내림차순 (가장 최근에 수정 한 항목) ( -t
)으로 인쇄하고, 디렉토리를 후행 /
으로 인쇄하여 ( -p
) 로 표시합니다 .grep -v '/$'
그런 다음 -v
후행 /
( /$
) 이있는 ( ) 행을 생략하여 결과 목록에서 디렉토리를 제거합니다 .
tail -n +6
처음 건너 뜁니다 (5 개) 모두를 반환 효과, 목록의 항목 만 5 개 가장 최근에 수정 된 파일 (있는 경우). 파일 N
N+1
tail -n +
xargs -I {} rm -- {}
(그리고 그 변형) rm
모든 파일 을 불러 옵니다; 일치 xargs
하는 항목이 없으면 아무 것도하지 않습니다.
xargs -I {} rm -- {}
{}
각 입력 줄 을 전체로 나타내는 자리 표시자를 정의 하므로 rm
각 입력 줄마다 한 번 호출되지만 포함 된 공백이있는 파일 이름을 사용하여 올바르게 처리됩니다.--
모든 경우에 시작하는 모든 파일 이름이 옵션 으로 -
오인되지 않도록 합니다 .rm
일치하는 파일을 개별적 으로 처리 하거나 셸 배열로 수집 해야하는 경우 원래 문제 의 변형 :
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
ls
현재 디렉토리에 있지, 다음 파일의 경로가 포함됩니다 '/', 어떤 수단 grep -v '/'
아무것도 일치하지 것이다. 나는 grep -v '/$'
디렉토리 만 제외하고 싶다고 생각 합니다.
grep
명령을 개념적으로 명확하게 만듭니다. 그러나 설명하는 문제 가 단일 디렉토리 경로로 표시 되지 않았을 것입니다 . 예를 들어, ls -p /private/var
여전히 단순한 파일 이름 만 인쇄합니다. 여러 파일 인수 (일반적으로 glob를 통해) 를 전달한 경우에만 출력에 실제 경로가 표시됩니다. 예를 들어, ls -p /private/var/*
포함하지 않은 경우 일치하는 하위 디렉토리의 내용도 볼 수 있습니다 -d
.
디렉토리에서 가장 최근 파일 5 개 (또는 숫자)를 제외한 모든 파일을 제거하십시오.
rm `ls -t | awk 'NR>5'`
ls -t
에ls -td *.bz2
ls -t | awk 'NR>1'
. 감사!
ls -t | awk 'NR>5' | xargs rm -f
파이프를 선호하고 삭제할 것이 없으면 오류를 억제해야합니다.
touch 'hello * world'
이 삭제 됩니다.
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm
이 버전은 공백이있는 이름을 지원합니다.
(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
--no-run-if-empty
에서 xargs
와 같이 추가 (ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
하여 답변을 업데이트하십시오.
touch 'foo " bar'
. 나머지 명령은 모두 버리게됩니다.
xargs -d $'\n'
(이외의 다른 사용하여 필요로하는 입력 스트림 NUL이-구분하지만, 콘텐츠에 주사한다 시세보다 ls
에 정말를 바로 않음) 이상적인 옵션입니다.
thelsdj의 답변의 간단한 변형 :
ls -tr | head -n -5 | xargs --no-run-if-empty rm
ls -tr은 가장 오래된 것부터 모든 파일을 표시합니다 (-t newest, -r reverse).
head -n -5는 5 개의 마지막 행을 제외한 모든 행을 표시합니다 (예 : 5 개의 최신 파일).
xargs rm은 선택된 각 파일에 대해 rm을 호출합니다.
-1
출력이 파이프 라인에있을 때는 @AlJoslin 이 기본값이므로 여기서는 필수가 아닙니다. xargs
공백, 따옴표, & c로 이름을 구문 분석 할 때의 기본 동작과 관련하여 훨씬 더 큰 문제 가 있습니다.
--no-run-if-empty
내 껍질에서 인식되지 않는 것 같습니다 . Windows에서 Cmder를 사용하고 있습니다.
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f
GNU print -printf, GNU sort -z, GNU awk "\ 0", GNU xargs -0에는 필요하지만 개행이나 공백이있는 파일을 처리합니다.
awk
논리 의 복잡성 (또는 그 문제의 필요성)에 놀랐습니다 . OP 질문에 필요한 몇 가지 요구 사항이 누락 되었습니까?
while read -r -d ' '; IFS= -r -d ''; do ...
루프 로 파이프 하는 것입니다. 첫 번째 읽기는 공간에서 종료되고 두 번째 읽기는 NUL로 진행됩니다.
sed -z -e 's/[^ ]* //; 1,5d'
가 가장 명확 하다고 생각 합니다. (또는 아마도 sed -n -z -e 's/[^ ]* //; 6,$p'
.
현재 디렉토리에 디렉토리가 있으면 이러한 모든 대답이 실패합니다. 다음은 작동하는 것입니다.
find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm
이:
현재 디렉토리에 디렉토리가있을 때 작동
이전 파일을 제거 할 수 없더라도 (권한 등으로 인해) 각 파일을 제거하려고합니다.
현재 디렉토리의 파일 수가 과도하고 xargs
일반적으로 ( -x
)
파일 이름에 공백을 넣지 않습니다 (아마도 잘못된 OS를 사용하고 있습니까?)
find
단일 명령 줄에서 전달할 수있는 것보다 많은 파일 이름을 반환 하면 어떻게됩니까 ls -t
? (힌트 : 여러 번의 실행을 얻습니다 ls -t
. 각 실행은 전체적으로 올바른 정렬 순서가 아닌 개별적으로 만 정렬되므로 충분히 큰 디렉토리로 실행할 때이 대답은 크게 손상되지 않습니다).
ls -tQ | tail -n+4 | xargs rm
각 파일 이름을 인용하여 수정 시간별로 파일 이름을 나열하십시오. 처음 3 개 (가장 최근 3 개)를 제외합니다. 남은 것을 제거하십시오.
mklement0의 유용한 의견 후 편집 (감사합니다!) : -n + 3 인수를 수정했으며 파일 이름에 줄 바꿈이 있거나 디렉토리에 하위 디렉토리가 있으면 예상대로 작동하지 않습니다.
-Q
옵션은 내 컴퓨터에 존재하지 않는 것 같습니다.
-Q
. 예, -Q
GNU 확장입니다 ( POSIX ls
사양 은 여기에 있습니다 ). 작은주의 (실제로는 거의 문제가) : -Q
임베디드 인코딩합니다 줄 바꿈 문자와 같은 파일 이름에 \n
, rm
인식하지 못합니다. 첫 번째 제외하려면 3 의 xargs
인수해야합니다 +4
. 마지막으로, 대부분의 다른 답변에도 적용되는 경고 : 현재 디렉토리 에 하위 디렉토리 가없는 경우 명령이 의도 한 대로만 작동 합니다.
--no-run-if-empty
옵션으로 xargs를 호출합니다 .ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
줄 바꿈을 무시하면 보안과 좋은 코딩이 무시됩니다. wnoise가 유일하게 좋은 대답을했다. 다음은 파일 이름을 $ x 배열에 넣는 변형입니다.
while IFS= read -rd ''; do
x+=("${REPLY#* }");
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
IFS
그렇지 않으면 파일 이름에서 공백을 잃을 위험이 있습니다. 읽기 명령으로 범위를 while IFS= read -rd ''; do
"${REPLY#* }"
?
파일 이름에 공백이 없으면 작동합니다.
ls -C1 -t| awk 'NR>5'|xargs rm
파일 이름에 공백이 있으면
ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh
기본 논리 :
while read
공백을 다루는 비결을 잊지 마십시오 : ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
조금 더 나은 것 - -r
방지에 의해 소비되는 리터럴 백 슬래시 read
및 IFS=
방지 자동 공백을 후행의 트리밍.
touch $'hello \'$(rm -rf ~)\' world'
;로 작성된 파일을 고려하십시오 . 파일 이름 안에있는 리터럴 따옴표는 추가 한 리터럴 따옴표에 대응 sed
하여 파일 이름 내의 코드가 실행되도록합니다.
| sh
는 셸 주입 취약점이있는 형식 을 나타냅니다 .)
zsh로
현재 디렉토리에 신경 쓰지 않고 999 개 이상의 파일이 없다고 가정합니다 (원하는 경우 더 큰 수를 선택하거나 while 루프를 작성하십시오).
[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])
에서 *(.om[6,999])
1, .
수단 파일의 o
수단 정렬, 위로 주문 m
(넣어 수정 날짜 수단을 a
액세스 시간이나 c
의는 아이 노드 변경) [6,999]
, 파일의 범위를 선택 그래서 먼저 5 RM은하지 않습니다.
om
)를 사용할 수 없었습니다 (내가 시도한 정렬은 효과가 없었습니다-OSX 10.11.2 (zsh 5.0.8 및 5.1.1로 시도). , 우분투 14.04 (zsh 5.0.2))-내가 무엇을 놓치고 있습니까?. 범위 엔드 포인트의 경우 : 하드 코딩 할 필요가 없으며 -1
마지막 항목을 참조하여 나머지 모든 파일을 포함하기 위해 사용 하십시오 [6,-1]
.
나는 이것이 오래된 실이라는 것을 알고 있지만 어쩌면 누군가가 이것으로부터 이익을 얻을 것입니다. 이 명령은 현재 디렉토리에서 파일을 찾습니다.
for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done
검색 도메인을 표현식과 일치하는 파일로 제한 할 수 있기 때문에 이전 답변 중 일부보다 약간 강력합니다. 먼저 원하는 조건과 일치하는 파일을 찾으십시오. 타임 스탬프 옆에 해당 파일을 인쇄하십시오.
find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'
그런 다음 타임 스탬프별로 정렬하십시오.
sort -r -z -n
그런 다음 목록에서 가장 최근 4 개의 파일을 차단하십시오.
tail -n+5
두 번째 열을 가져옵니다 (타임 스탬프가 아닌 파일 이름).
awk '{ print $2; }'
그런 다음 모든 것을 for 문으로 마무리하십시오.
for F in $(); do rm $F; done
이것은 더 자세한 명령 일 수 있지만 조건부 파일을 대상으로하고 더 복잡한 명령을 실행할 수있는 것이 훨씬 좋습니다.
Sed-Onliners에서 흥미로운 cmd를 찾았습니다-마지막 3 줄을 삭제하십시오-고양이를 껍질을 벗기는 다른 방법에 적합하지만 아이디어는 아닙니다.
#!/bin/bash
# sed cmd chng #2 to value file wish to retain
cd /opt/depot
ls -1 MyMintFiles*.zip > BigList
sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList
for i in `cat DeList`
do
echo "Deleted $i"
rm -f $i
#echo "File(s) gonzo "
#read junk
done
exit 0
10 개의 최신 (가장 최근) 파일을 제외한 모든 파일을 제거합니다
ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm
파일이 10 개 미만인 경우 파일이 제거되지 않으며 다음과 같은 결과가 나타납니다. 오류 헤드 : 잘못된 행 수-0
busybox (router)를위한 우아한 솔루션이 필요했습니다. 모든 xargs 또는 어레이 솔루션은 나에게 쓸모가 없었습니다. find와 mtime은 우리가 10 개의 항목에 대해 이야기하고 있기 때문에 반드시 10 일이 아니라는 정답이 아닙니다. Espo의 대답은 가장 짧고 깨끗했으며 아마도 가장 보편적 인 것이 었습니다.
공백이 있거나 파일을 삭제하지 않을 때의 오류는 단순히 표준 방법으로 해결됩니다.
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
좀 더 교육적인 버전 : awk를 다르게 사용하면 모든 것을 할 수 있습니다. 일반적 으로이 방법을 사용하여 변수를 awk에서 sh로 (반환) 전달합니다. 우리가 할 수없는 모든 시간을 읽었을 때 나는달라고 간청합니다 : 여기에 방법이 있습니다.
파일 이름의 공백과 관련하여 문제가없는 .tar 파일의 예 테스트하려면 "rm"을 "ls"로 바꾸십시오.
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
설명:
ls -td *.tar
시간별로 정렬 된 모든 .tar 파일을 나열합니다. 현재 폴더의 모든 파일에 적용하려면 "d * .tar"부분을 제거하십시오.
awk 'NR>7...
처음 7 줄을 건너 뜁니다.
print "rm \"" $0 "\""
라인을 구성합니다 : rm "file name"
eval
그것을 실행
우리는를 사용 rm
하고 있기 때문에 스크립트에서 위의 명령을 사용하지 않을 것입니다! 더 현명한 사용법은 다음과 같습니다.
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
ls -t
명령 을 사용하는 경우 다음 touch 'foo " bar'
과 같은 어리석은 예에는 아무런 영향을 미치지 않습니다.touch 'hello * world'
. 우리는 그런 이름을 가진 파일을 실제로 만들지 않습니다!
각주. 이 방법으로 변수를 sh에 전달하려면 인쇄를 수정하면됩니다 (간단한 형식, 공백은 허용되지 않음).
print "VarName="$1
변수 VarName
를 값 으로 설정합니다 $1
. 한 번에 여러 변수를 만들 수 있습니다. 이것은 VarName
일반적인 sh 변수가되고 나중에 스크립트 나 쉘에서 사용될 수 있습니다. 따라서 awk로 변수를 만들고 셸에 다시 제공하려면 다음을 수행하십시오.
eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\"" }'); echo "$VarName"
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))
# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0
ls -t *.log | tail -$tailCount | xargs rm -f
xargs
없이 -0
또는 최소한으로하는 것은 -d $'\n'
신뢰할 수 없다; 공백이나 따옴표 문자가있는 파일에서 이것이 어떻게 동작하는지 관찰하십시오.
나는 이것을 bash 쉘 스크립트로 만들었다. 사용법 : keep NUM DIR
여기서 NUM은 보관할 파일 수이고 DIR은 제거 할 디렉토리입니다.
#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
echo "Usage: $0 NUMFILES DIR"
echo "Keep last N newest files."
exit 1
fi
if [ ! -e $2 ]; then
echo "ERROR: directory '$1' does not exist"
exit 1
fi
if [ ! -d $2 ]; then
echo "ERROR: '$1' is not a directory"
exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l