Remova todos os arquivos, exceto o maior

0

Eu tenho uma pasta com muitas subpastas. Desejo remover todos os arquivos menores de cada subpasta, deixando apenas o arquivo maior.

Por exemplo:

Subfolder1
---------- File 1 ---- 300k
---------- File 2 ---- 299k
---------- File 3 ---- 800k

Apenas file 3 deve permanecer com 800k. Se a pasta tiver apenas um arquivo, ele permanecerá.

Esse código funciona, mas não consigo colocá-lo em um loop for (para o diretório recursivo):

find . -type f -maxdepth 1 | sort -n -r | tail -n +2 | xargs -I{} rm -v {}

Como posso fazer isso?

    
por Lordjohnny 27.05.2017 / 17:23

2 respostas

0
~$ 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 lista por diretório e, em seguida, por tamanho decrescente.

  • A primeira linha de código de awk ignora as linhas de 1a linha ou da tree com barras à direita (ou seja, diretórios)
  • A segunda linha de código do awk constrói um dirname a partir do caminho completo ( for loop), depois imprime os nomes dos caminhos completos se o dirname foi encontrado uma vez nas linhas anteriores (isto é, imprime para cada diretório)
por 28.05.2017 / 07:46
0

Justificação

Esta é minha tentativa de criar um comando que funcione com qualquer diretório e nome (s) de arquivo. Em geral, os caminhos no Linux (e nomes nos sistemas de arquivos) podem conter qualquer caractere, mas nulo ( 0x00 ) e / . Caracteres problemáticos podem ser " " (espaço), qualquer outro caractere branco, ' , " , nova linha, outros caracteres não imprimíveis. Portanto, é importante:

  • abandonam ferramentas que substituem alguns caracteres por outras (por exemplo, muitas implementações de ls imprimem ? para não-imprimíveis);
  • passa todos os nomes como sequências terminadas em nulo (escolha ferramentas que possam analisá-las);
  • cite corretamente.

Eu fui inspirado pela discussão em esta outra resposta .

Comandos reais

Versão de teste, serão apenas ls os arquivos que seriam removidos:

find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n
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
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n
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%pre%" \{\} + |   # ...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).
" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm
" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l
" \{\} + | # ...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).
" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm
" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l

Sim, estou usando ls aqui, apesar do que acabei de dizer. Isso ocorre porque ls output não está sendo analisado mais. Estou usando apenas para exibir o resultado. Se acontecer de você ter diretórios ou arquivos com caracteres problemáticos em seus nomes, você observará o comportamento de ls , que deve convencê-lo a nunca analisar ls (a menos que você saiba que está absolutamente seguro disso). Ainda assim, os nomes problemáticos passarão até ls e esse é o ponto.

Entenda a versão de teste (veja abaixo algumas explicações) e experimente antes de deixar a versão de trabalho (logo abaixo) remover seus arquivos. Lembre-se de que sou apenas um cara aleatório na Internet.

Versão de trabalho, ele removerá seus arquivos:

%pre%

Explicação

Aqui está a versão de testes dividida em várias linhas (embora ainda seja uma linha para bash ; note que eu uso esse truque para comentários inline):

%pre%

Técnicas usadas, obstáculos superados:

  • As ferramentas que analisam strings são instruídas a trabalhar com sequências terminadas com nulo:
    • stat --printf "…sort -z" ;
    • tail -z , cut -z , xargs -0 … ;
    • find -print0 ;
    • sh -c '…' (não é necessário neste exemplo, mas é muito comum em geral, portanto eu menciono isso de qualquer maneira).
  • find -exec é o caminho para usar pipes dentro de find -type d -exec sh -c 'find "{}" … .
  • " será quebrado para o nome do diretório contendo find -type d -exec sh -c 'find "$0" … ' {} \; ; {} funciona bem.
  • find na instrução \{\} interna tem escape ( find ) para evitar que o% externocut os substitua.
  • tail poderia seguir imediatamente cut , ele executaria um find por diretório. Colocá-lo fora do exterior cut faz com que um único -r faça todo o corte de uma só vez.
  • A opção xargs para ls impede que rm ( xargs na versão de trabalho) seja executado quando não há entrada para %code% .
por 10.07.2017 / 10:35

Tags