Como mesclar as cores dos comandos ls com a saída de find ou du?

Como mesclar as cores dos comandos ls com a saída de find ou du?

Eu tenho um alias simples para listar o tamanho dos arquivos e pastas no cwd.

(incluindo dotfiles neste caso, ignorando tamanhos zero)

du -sh .[!.]* * | sort -hr | grep -v '^0'

o que também pode ser alcançado com find:

find .[!.]* * -maxdepth 0 -exec du -sh {} \; | sort -hr | grep -v '^0'

saída de exemplo:

// .ssh & .byobu are folders - .zsh* are files
// currently not able to distinguish them by type

... 
32K     .zshrc
25K     .ssh
20K     .zcompdump
19K     .byobu
...

Como posso colorir os arquivos/diretórios na saída correspondendo às cores ls?(LS_COLORS)

Responder1

Este zshscript analisa $LS_COLORS. Ele precisa apenas de uma statchamada para cada arquivo e, portanto, é muito mais rápido que a solução abaixo, que chama lstodos os arquivos. E lida corretamente com arquivos com espaços. ( \nou \tainda estãonãopermitido em nomes de arquivos)

No entanto, a implementação não está completa. Incluí apenas cores para diferentes tipos de arquivos que podem ser identificados pelo primeiro caractere da string do modo de arquivo (por exemplo, lrwxrwxrwxpara um link simbólico) ou pela extensão do arquivo. Isso significa que as permissões graváveis ​​em todo o mundo, suid ou bits pegajosos não são coloridos de maneira especial. Incluí-los também deve ser simples.

sourceo seguinte e use a nova função shell ducpara uma dusaída colorida:

zmodload -F zsh/stat b:zstat

function duc() {

  emulate zsh 
  setopt no_nomatch interactivecomments           # no_nomatch is necessary, to prevent error in "do .* *" if there are no dotfiles

  typeset -a aline
  typeset -A lscols
  local dircols acolor

  for i (${(s.:.)LS_COLORS}) {                    # split $LS_COLORS at ":"
    aline=(${(s:=:)i})                            # split every entry at "="
    lscols+=(${${aline[1]}/*.} ${aline[2]})       # load every entry into the associative array $lscols
  }

  duout=$(du -sh .* * 2> /dev/null | grep -v '^0' | sort -hr)
  for i (${(f)duout}) {                           # split output of "du" at newlines
    aline=(${(ps:\t:)i})                          # split every entry at \t
    zstat -s +mode -A atype ${aline[2]}           # determine mode (e.g. "drwx------") of file ${aline[2]}
    case ${${atype[1]}[1]} in                     # ${${atype[1]}[1]} is the first character of the file mode
      b)   acolor=$lscols[bd] ;;
      c|C) acolor=$lscols[cd] ;;
      d)   acolor=$lscols[di] ;;
      l)   acolor=$lscols[ln] ;;
      p)   acolor=$lscols[pi] ;;
      s)   acolor=$lscols[so] ;;
      -)   acolor=${lscols[${${aline[2]}:e}]};      # ${${aline[2]}:e} is the current file extention
           [[ -z $acolor ]] && acolor=$lscols[fi]   # unrecognized extention
           [[ -z $acolor ]] && acolor=00            # sometimes "fi" isn't set in $LS_COLORS, so fall back to normal color
           ;;
      *)   acolor=00 ;;
    esac
    print -n -- "${aline[1]}\t"        # print size (taken from du output)
    print -n "\\e[4${acolor}m"         # activate color
    print -n ${aline[2]}               # print file name
    print "\\e[0m"                     # deactivate color
  }
}

Este é meu script antigo, também para zsh. Provavelmente é desnecessariamente complexo e muito lento, pois para cada arquivo lsé emitido um único comando:

du_colored() {
  typeset -a duout
  duout=($(du -sh .* * | sort -hr | grep -v '^0'))
  for i ({1..$#duout..2}) {
    print -n "${duout[$i]}\t"
    ls -d --color ${duout[$(($i+1))]}
  }
}
  • o .*testamentozshnãomatch .ou .., mas arquivos como ..foowhich serão perdidos com.[!.]*
  • typeset -adeclara uma matriz
  • os loops for no array, $iassume valores de 1 em diante em etapas de 2

Aviso: Isso irá quebrar muito quando houver arquivos comespaços em branco... Não tenho ideia melhor no momento.

Responder2

Isto é o que eu criei para o bash - usa pv para mostrar o progresso da saída

folder_size (){
  # read ls --color output into ls_colored_array 
  # thereby remove symlinks (@) and remove (/ and *) from folders and files

  ls -AF --color | grep -v @ | sed s'/[/\,*]$//'| xargs -n1 -L1 | read -d '\n' -r -a ls_colored_array

  # - loop over the array and issue du -sh for every element
  # - exchange du's ouput with ls's 
  # - run loop through pv with line option and
  #    size set to array-length showing progress
  # - finally sort the output using sort

  for i in "${ls_colored_array[@]}"; do
    echo -n "${i}" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | xargs -n1 -0 du -sh | awk -v i="$i" '{ printf "%-10s ", $1; $1=""; print i }'
  done | pv -pls"${#ls_colored_array[@]}" | sort -hr
}

O script agora pode ser transformado em uma linha ... mas vou melhorar a função adicionando sinalizadores para, ou seja, 10 arquivos/pastas maiores ou apenas pastas.

Responder3

Isso funciona para mim:

#!/usr/bin/env bash

paste <(du --apparent-size --all --human-readable --max-depth=1 2>/dev/null |
    sed 's/\s.*//') \
    <(ls --color=always -1 --almost-all -U) | sort --human-numeric-sort

O comando principal aqui é paste, ele unirá as saídas de lse duhorizontalmente.

duproduzirá cada linha mais ou menos assim:

147291    ./.config

sed 's/\s.*//irá deletar tudo depois e inclusive o primeiro espaço, ou seja, ./.configjá que não estamos interessados ​​nas formas não coloridas dos nomes das entradas listadas. O truque aqui é gerar lso conteúdo do diretório da mesma maneira que dupodemos unir facilmente cada entrada com seu tamanho correspondente. Podemos fazer isso com a ajuda de -1(que lista as entradas verticalmente em vez de horizontalmente), -U(que lista as entradas em ordem de diretório, assim como du, em vez de alfabeticamente) e --almost-all(que lista diretórios/arquivos ocultos também, mas deixa de fora .e ..em oposição a --all) e claro --color=always. Para du, --allé necessário fazer com que ele processe arquivos (incluindo os ocultos) e diretórios em vez de apenas diretórios, e --max-depth=1pare dude operar em subdiretórios (assim findcomo -maxdepth 1)

Portanto, para responder à pergunta, basta o seguinte:

paste <(du --all --max-depth=1 | sed 's/\s.*//') \
    <(ls --color=always -1 --almost-all -U)

Quanto aos outros argumentos opcionais:

  • --apparent-sizetorna duos tamanhos de impressão aparentes, em vez do uso do disco (minha preferência)
  • --human-readableimprime tamanhos legíveis por humanos
  • 2>/dev/nullfiltra quaisquer permission deniederros se você tentou executar duem um diretório restrito, por exemplo/var
  • sort --human-numeric-sortclassifica os tamanhos em ordem decrescente, --human-numeric-sorté necessário se --human-readablefor passado paradu

Responder4

Combinar algumas das respostas acima com oh-my-zshell em um Mac me forneceu tamanhos, nomes de diretórios coloridos e nomes de arquivos de texto simples classificados para gerar os 10 maiores arquivos/diretórios:

paste <(du -cks *) <(ls --color=always -1) | sort -nr | head | awk '{print $1 "  " $3}'

Explicação:
dupara uso do disco -c:grand total -k: 1Kb blocks -s: entrypara cada arquivo;
lspara pegar a cor dos diretórios;
pastecombinar ambos os resultados acima;
sort -nr: classificação reversa numérica;
head: gera os maiores arquivos/diretórios;
awk: para gerar apenas a primeira coluna (tamanho) e a terceira coluna (arquivo/nome do diretório colorido)

informação relacionada