
하위 폴더가 많은 폴더가 있습니다. 각 하위 폴더에서 작은 파일을 모두 제거하고 가장 큰 파일만 남기고 싶습니다.
예를 들어:
Subfolder1
---------- File 1 ---- 300k
---------- File 2 ---- 299k
---------- File 3 ---- 800k
file 3
800k 만 남아 있어야 합니다. 폴더에 파일이 하나만 있으면 해당 파일은 그대로 유지됩니다.
이 코드는 작동하지만 for 루프(디렉토리 재귀의 경우)에 넣을 수 없습니다.
find . -type f -maxdepth 1 | sort -n -r | tail -n +2 | xargs -I{} rm -v {}
어떻게 해야 하나요?
답변1
~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"
4 directories, 8 files
~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
> {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}'
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"
~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
> {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}' | \
> xargs rm -v
'pluto/pluto1/pluto3/zero ed.txt' rimosso
'pluto/pluto2/nozer.txt' rimosso
'pluto/pluto2/zero.txt' rimosso
~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"
4 directories, 5 files
tree
디렉토리별로 나열한 다음 크기를 내림차순으로 나열합니다.
awk
의 첫 번째 코드 줄은tree
'의 출력 첫 번째 줄을 건너뜁니다.또는뒤에 슬래시가 있는 줄(예: 디렉토리)awk
코드의 두 번째 줄은 전체 경로(for
루프)에서 dirname을 만든 다음 dirname이 이전 줄에서 한 번 발견된 경우 전체 경로 이름을 인쇄합니다(즉, 두 번째 나열된 파일부터 각 디렉터리에 대해 인쇄합니다).
답변2
정당화
이것은 다음과 함께 작동하는 명령을 작성하려는 나의 시도입니다.어느디렉토리 및 파일 이름. 일반적으로 Linux의 경로(및 파일 시스템의 이름)에는 null( 0x00
) 및 /
. 문제가 되는 문자는 " "(공백), 기타 흰색 문자,
'
, "
, 개행 문자, 기타 인쇄할 수 없는 문자 일 수 있습니다 . 따라서 다음 사항이 중요합니다.
- 일부 문자를 다른 문자로 바꾸는 도구를 포기합니다(예: 인쇄할 수 없는 항목에 대해
ls
인쇄할 것의 많은 구현?
). - 모든 이름을 null로 끝나는 문자열로 전달합니다(해당 이름을 구문 분석할 수 있는 도구 선택).
- 제대로 인용해라.
나는 아래의 토론에서 영감을 얻었습니다.이 다른 답변.
실제 명령
ls
테스트 버전에서는 제거될 파일 만 표시됩니다 .
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l
ls
예, 방금 말한 내용에도 불구하고 저는 여기를 사용하고 있습니다 . 이는 ls
출력이 더 이상 구문 분석되지 않기 때문입니다. 결과를 표시하는 데에만 사용하고 있습니다. 이름에 문제가 있는 문자가 포함된 디렉터리나 파일이 있는 경우, 그 동작을 관찰하면 다음 ls
과 같은 확신이 들 것입니다.절대 파싱하지 마세요ls
(당신이 그것으로 절대적으로 안전하다는 것을 알지 않는 한). 그래도 귀찮은 이름은 끝까지 지나갈 것이고 ls
이것이 요점입니다.
테스트 버전 이해(설명은 아래 참조)작업 버전을 사용하기 전에 먼저 시도해 보세요.(바로 밑)파일을 제거하십시오.나는 단지 인터넷상의 무작위 사람이라는 것을 기억하십시오.
작업 버전에서는 파일이 제거됩니다.
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm
설명
다음은 여러 줄로 분할된 테스트 버전입니다(여전히 한 줄이지만 bash
; 참고로 저는이 트릭주석을 인라인하려면):
find -type d -exec `# Find all directories under (and including) the current one.` \
sh -c ' `# In every directory separately...` \
find "$0" -maxdepth 1 -mindepth 1 -type f -exec `# ...find all files,...` \
stat --printf "%s %n\0" \{\} + | # ...get their sizes and names,...
sort -znr | # ...sort by size...
tail -zn +2' `# ...and discard the "biggest" entry.` \
{} \
\; | # (All the directories have been processed).
cut -zf 2- -d " " | # Then extract filenames...
xargs -0r ls -l # ...and ls them (rm in the working version).
사용된 기술, 장애물 극복:
- 문자열을 구문 분석하는 도구는 null로 끝나는 문자열과 함께 작동하도록 지시됩니다.
stat --printf "…\0"
;sort -z
,tail -z
,cut -z
;xargs -0 …
;find -print0
(이 예에서는 필요하지 않지만 일반적으로 매우 일반적이므로 어쨌든 언급하겠습니다).
sh -c '…'
내부에 파이프를 사용하는 방식입니다find -exec
.find -type d -exec sh -c 'find "{}" …
다음을 포함하는 디렉토리 이름으로 인해 중단됩니다"
.find -type d -exec sh -c 'find "$0" … ' {} \;
잘 작동합니다.{}
내부 문에서는 외부 문이 이를 대체하는 것을 방지하기 위해find
이스케이프( )됩니다 .\{\}
find
cut
즉시 따라갈 수 있으면 디렉터리당tail
하나씩 실행됩니다cut
. 외부에 배치하면 한 번에 모든 절단 작업을find
수행할 수 있습니다 .cut
- 에 대한 입력이 없을 때 ( 작업 버전에서) 실행을 방지하는
-r
옵션 입니다 .xargs
ls
rm
xargs