문제

bash를 사용하는 매우 표준적인 UNIX 환경에서 디렉토리에서 가장 최근의 X 파일을 제외한 모든 파일을 삭제하는 명령을 실행하는 간단한 방법이 있습니까?

좀 더 구체적인 예를 들기 위해 매 시간마다 디렉토리에 파일(예: 로그 파일 또는 tar 백업)을 작성하는 일부 cron 작업을 상상해 보십시오.예를 들어 5개 미만이 될 때까지 해당 디렉터리에서 가장 오래된 파일을 제거하는 또 다른 cron 작업을 실행하는 방법을 원합니다.

그리고 명확하게 말하면 파일이 하나만 있으므로 삭제하면 안 됩니다.

도움이 되었습니까?

해결책

기존 답변의 문제점:

  • 공백이나 개행 문자가 포함된 파일 이름을 처리할 수 없습니다.
    • 호출하는 솔루션의 경우 rm 인용되지 않은 명령 대체에 직접적으로(rm `...`), 의도하지 않은 글로빙(globbing)의 위험이 추가됩니다.
  • 파일과 디렉토리를 구별할 수 없음(예: 디렉토리 가장 최근에 수정된 5개의 파일 시스템 항목 중 하나였으므로 효과적으로 유지할 수 있습니다. 보다 적은 5개 파일 이상, 신청 rm 디렉터리에 대한 작업은 실패합니다).

Wnoise의 답변 이러한 문제를 해결하지만 해결책은 암소 비슷한 일종의 영양-특정(그리고 상당히 복잡함).

여기에 실용적인 것이 있습니다. POSIX 호환 솔루션 그것만 함께 제공됩니다 한 가지 주의사항:포함된 파일 이름을 처리할 수 없습니다. 개행 문자 - 하지만 나는 그것이 대부분의 사람들에게 실제적인 관심사라고 생각하지 않습니다.

기록을 위해 일반적으로 구문 분석하는 것이 좋지 않은 이유에 대한 설명은 다음과 같습니다. ls 산출: http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

위의 내용은 무능한, 왜냐하면 xargs 불러내야 한다 rm 한 번만 파일 이름.
귀하의 플랫폼 xargs 이 문제를 해결할 수 있습니다.

당신이 가지고 있다면 암소 비슷한 일종의 영양 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 X), 당신이 사용할 수있는 -0 다루다 NUL-개행 문자를 먼저 번역한 후 분리된 입력 NUL (0x0) chars.(일반적으로) 모든 파일 이름도 전달합니다. 한 번에 (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

다른 팁

디렉토리에서 가장 최근 파일의 5 개 (또는 숫자)를 모두 제거하십시오.

rm `ls -t | awk 'NR>5'`
(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

THELSDJ의 답변의 간단한 변형 :

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

ls -tr은 모든 파일, 가장 오래된 첫 번째 파일을 표시합니다 (-t 최신 첫, -r 리버스).

Head -N -5는 5 개의 마지막 줄을 제외한 모든 것을 표시합니다 (예 : 5 개의 최신 파일).

XARGS RM은 선택한 각 파일에 대해 RM을 호출합니다.

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는 -printf에 대한 GNU를 찾고 -z에 대해 GNU 정렬, " 0"에는 GNU awk, -0의 경우 gnu xargs가 필요하지만 새로운 신형 또는 공간이 포함 된 파일을 처리해야합니다.

현재 디렉토리에 디렉토리가있을 때 이러한 모든 답변이 실패합니다. 다음은 작동하는 것입니다.

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

이것:

  1. 현재 디렉토리에 디렉토리가있을 때 작동합니다

  2. 이전 파일을 제거 할 수없는 경우에도 각 파일을 제거하려고 시도합니다 (권한으로 인해).

  3. 현재 디렉토리의 파일 수가 과도하고 xargs 일반적으로 당신을 망칠 것입니다 ( -x)

  4. 파일 이름의 공간을 수용하지 않아도됩니다 (아마도 잘못된 OS를 사용하고 있습니까?)

ls -tQ | tail -n+4 | xargs rm

각 파일 이름을 인용하여 수정 시간별로 파일 이름을 나열하십시오. 처음 3 (가장 최근 3 개) 제외. 나머지를 제거하십시오.

mklement0 (감사합니다!)에서 유용한 주석 후 편집 : 수정 된 -n+3 인수는 filename에 newlines 및/또는 디렉토리에 하위 디렉토리가 포함 된 경우 예상대로 작동하지 않습니다.

Newlines를 무시하는 것은 보안과 좋은 코딩을 무시하고 있습니다. Wnoise는 유일한 좋은 대답을 가지고있었습니다. 다음은 파일 이름을 배열 $ x에 넣는 그의 변형입니다.

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )

파일 이름에 공간이 없으면 다음이 작동합니다.

ls -C1 -t| awk 'NR>5'|xargs rm

파일 이름에 공백이있는 경우

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

기본 논리 :

  • 파일 목록을 시간 순서대로 한 열 1 열 목록 가져옵니다.
  • 처음 5를 제외한 모든 것을 얻으십시오 (이 예제의 N = 5)
  • 첫 번째 버전 : RM으로 보내십시오
  • 두 번째 버전 : 제대로 제거 할 스크립트 gen

ZSH와 함께

현재 디렉토리에 신경 쓰지 않으며 999 개를 넘지 않는다고 가정하면 (원하는 경우 더 큰 번호를 선택하거나 while 루프를 작성하십시오).

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

*(.om[6,999]),, . 파일을 의미합니다 o 정렬 순서를 의미합니다 m 수정 날짜별로 (put a 액세스 시간 또는 c Inode Change), The [6,999] 파일 범위를 선택하므로 5를 먼저 rm하지 않습니다.

나는 이것이 오래된 실이라는 것을 알고 있지만 누군가가 이것으로부터 혜택을받을 것입니다. 이 명령은 현재 디렉토리에서 파일을 찾을 수 있습니다.

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 F in $(); do rm $F; done

이것은 더 장점의 명령 일지 모르지만 조건부 파일을 대상으로하고 더 복잡한 명령을 실행할 수있는 운이 훨씬 더 좋았습니다.

SED -ONLINERS에서 흥미로운 CMD를 발견했습니다 - 마지막 3 줄 삭제 -FND는 고양이를 피부에 다른 방법으로 완벽하게 (좋아요), 아이디어 :

 #!/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

Bash가있는 파일을 계산합니다

BusyBox (라우터)를위한 우아한 솔루션이 필요했고, 모든 XARG 또는 배열 솔루션은 나에게 쓸모가 없었습니다. 그러한 명령은 없습니다. Find and Mtime은 우리가 10 일에 대해 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 "파일 이름"

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

나는 이것을 배쉬 쉘 스크립트로 만들었다. 용법: keep NUM DIR 여기서 Num은 보관할 파일 수입니다.

#!/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

Debian에서 달리기 (내가 얻는 다른 배포판에서도 동일하다고 가정합니다. rm : 디렉토리`.. '를 제거 할 수 없습니다.

꽤 성가신 ..

어쨌든 나는 위를 조정하고 명령에 Grep을 추가했습니다. 이 경우 디렉토리에 6 개의 백업 파일이 있습니다. 예를 들어 File1.tar file2.tar file3.tar 등이 있으며 가장 오래된 파일 만 삭제하려고합니다 (이 경우 첫 번째 파일을 제거).

가장 오래된 파일을 삭제하기 위해 실행 한 스크립트는 다음과 같습니다.

ls -c1 -t | grep 파일 | awk 'nr> 5'| xargs rm

이것은 내 파일의 첫 번째 파일을 삭제합니다. 즉

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top