2011-12-15 1 views
5

데비안에서 스크립트로 트리에서 가장 오래된 파일을 삭제하려고합니다. (? 또는 거기가 더 쉬운/더 나은 방법입니다) :xargs와 find, rm이 파일 이름에 n (개행)을 불평 함

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -print0 | xargs -0 ls -t | tail -1 | xargs -0 rm 

는하지만 오류가 점점 오전

rm: cannot remove `/home/backups/tree/structure/file.2011-12-08_03-01-01.sql.gz\n': No such file or directory 

내가 잘못 뭐하는 거지 어떤 아이디어, 나는 RTFM에 시도를하지만, 손실입니다.

+1

당신은 생략 할 경우'-0' 마지막'xargs'에서? – sjngm

답변

11

ls로 사용을 줄 바꿈 마지막 용 xargs 추가 -0은 줄 바꿈이 파일 이름의 일부임을 나타냅니다. -0 대신 -d '\n'을 사용하여 마지막 xargs를 실행하십시오.

그런데 xargs가 작동하는 방식 때문에 전체 파이프가 대기중인 버그입니다. find에 의해 생성되는 매우 긴 파일 이름 목록을 고려하여 xargs -0 ls이 파일 이름의 하위 집합과 함께 ls을 여러 번 실행합니다. 중 가장 오래된 것만 ls을 호출하면이 호출되어 tail -1을 지나게됩니다. 예를 들어, 가장 오래된 파일이 실제로 첫 번째 파일 이름 출력이 find 인 경우 더 오래된 파일을 삭제합니다.

+1

그건 충분하지 않습니다. '-d '가 없으면 \ n'','xargs'가 모든 공백에 나뉘어집니다. 이것은 아마도 여러분이 원하는 것이 아닙니다. – glglgl

+0

완벽한, 감사합니다. 그런데 "정말 긴 파일 이름 목록"으로 간주되는 것은 무엇입니까? – user1076412

+0

@ user1076412 :이 경우 "긴"은 OS가 설정하는 "ARG_MAX보다 큼"을 의미합니다. 'find '에 의해 리턴 된 모든 파일 이름이 ARG_MAX보다 길면 xargs는 두 번 이상 명령을 호출합니다. – Sorpigal

1

ls은 줄 바꿈 문자를 방출하므로 두 번째 xargs -0xargs -d '\n'으로 바꿔야합니다. 가장 오래된 파일의 이름에 개행 문자가 있으면 중단됩니다.

0

ls -t 포인트를 놓쳤습니다.

나는 훨씬 더 간단하게 제안 할 수 있습니다.

find /home/backups \ 
    -type f -iregex '.*\.t?gz$' \ 
    -mtime +60 -exec rm {} \; 
당신은 tail을 사용하지만, 널 (null) 종료를 찾기 위해 그것을 말하지 않았다


(예 60 일) 특정 연령보다 나이가 일치하는 파일 삭제됩니다

구분 기호. 에 관계없이

, 여기에 마지막 0으로 구분 된 요소를 반환하는 데 사용할 수있는 폴더의 유틸리티입니다 :

#include <string> 
#include <iostream> 
#include <cstdio> 

int main(int argc, const char *argv[]) 
{ 
    std::cin.unsetf(std::ios::skipws); 
    if (! (freopen(NULL, "wb", stdout) && freopen(NULL, "rb", stdin))) 
    { 
     perror("Cannot open stdout/in in binary mode"); 
     return 255; 
    } 

    std::string previous, element; 
    while (std::getline(std::cin, element, '\0')) 
    { 
     previous = element; 
     // if you have c++0x support, use this _instead_ for performance: 
     previous = std::move(element); 
    } 

    std::cout << previous << '\0' << std::flush; 
} 

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -print0 | ./mytail | xargs -0 rm 
+0

첫 번째'xargs'는 null 구분 기호와'ls' 구분 기호를 줄 바꿈하므로, 꼬리는 괜찮습니다. – thiton

+0

좋은 생각이지만,이 질문에는 일종의 누락이 있습니다 (OP에서'ls -t'). – thiton

+0

@thiton : 그것을 고쳤습니다 – sehe

0

ls -tr $(find /home/backups -name '*.gz' -o -name '*.tgz')|head -1|xargs rm -f

+1

공백 문자가 너무 길거나 파일 목록이 너무 길어집니다. – thiton

2

또한, 정렬, 수정 시간을 인쇄 마음대로 잘라 xargs를 찾을 수 있습니다 :

find /home/backups -printf "%[email protected] %p\n" | sort -n | head -1 | cut -d" " -f2- | xargs ls -al 
+0

이것은 최선의 방법이지만, 고정 된 오프셋에 의존하기보다는 구분자에서'잘라 내기 '할 것입니다. – Sorpigal

+0

내 감기, 죽은 손에서 내 "svn status | cut -c8-"상을 수여 할 수 있습니다! 그렇지만 구분 기호가 더 좋을 것입니다. – Ken

+0

편집 결과에 새로운 버그가 있습니다. 파일 이름에 공백이 있으면'-f2-'라고 써야합니다. – Sorpigal

3

ls 관련된 모든 솔루션이 절대적으로 잘못된 것입니다.

이 작업을 수행하는 올바른 방법은 연대순을 주문 삭제 한 후, 첫 번째 제외한 rm 모두를 필터링, sort을 파일 세트를 가져 find을 사용하는 것입니다. @Ken은이 부분이 대부분 옳았으며 몇 가지 세부 사항 만 누락되었습니다.

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -printf '%[email protected] %p\0' |\ 
    sort -z -n | \ 
    { IFS= read -d '' file ; [ -n "$file" ] && echo rm -f "$(cut -d' ' -f2- <<<"$file")" ; } 

위의 echo을 제거하여 실제로 삭제를 수행합니다.

위 코드는 파일 이름에 공백, 개행 문자 또는 기타 비정상적인 값이있는 파일에도 적용됩니다. 결과가 없을 때 유해한 것도하지 않습니다. 당신은 파일 이름에 줄 바꿈에 침입 신경 쓰지 않는 경우

이 차이는 우리가 head에 의존 할 수 대신 미친 아무것도의 파이프에 cut를 사용할 수 있다는 것입니다

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -printf '%[email protected] %p\n' |\ 
    sort -n |\ 
    head -n 1 |\ 
    cut -d' ' -f2- |\ 
    xargs echo rm 

좀 더 쉽게 얻을 수 있습니다. 에서

+1

+1이없는 올바른 해결책은 +1입니다. – l0b0