format saída de comando com espaço variável e fixo

1

Como posso conseguir isso mantendo a hierarquia à esquerda e alinhando o valor de tamanho à direita?
Além disso, ao mesmo tempo, gostaria de arredondar o tamanho para um ou dois dígitos.
Eu tentei com printf , mas não consegui descobrir como manter esse formato.

$ port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*//" | while IFS= read -r line; do echo -e "$line" \t\t\t\t $(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2); done
mtr                  0.088 MiB
  pkgconfig                  0.615 MiB
    libiconv                 6.270 MiB
      gperf                  0.0 MiB
  glib2                  46.092 MiB
    xz               1.679 MiB
      gettext                24.825 MiB
        expat                1.109 MiB
        ncurses                  15.171 MiB
    libxml2                  10.405 MiB
      zlib               0.721 MiB
    libffi               0.141 MiB
    pcre                 5.954 MiB
      bzip2                  0.647 MiB
      libedit                0.795 MiB

Isso deve se parecer com isso

mtr                          0.08 MiB
  pkgconfig                  0.61 MiB
    libiconv                 6.27 MiB
      gperf                  0.00 MiB
  glib2                     46.09 MiB
    xz                       1.67 MiB
      gettext               24.82 MiB
        expat                1.10 MiB
        ncurses             15.17 MiB
    libxml2                 10.40 MiB
      zlib                   0.72 MiB
    libffi                   0.14 MiB
    pcre                     5.95 MiB
      bzip2                  0.64 MiB
      libedit                0.79 MiB
    
por 1.61803 26.03.2016 / 23:56

2 respostas

1

Aqui estão algumas abordagens:

com arredondamento

Como você parece perceber, printf é uma ótima ferramenta para impressão formatada. %f é o formato para produzir um “número de ponto flutuante”, ou seja, um número que não é um inteiro, isto é, um número que inclui uma parte fracionária. Como em todos os formatos printf, um número (um inteiro!) imediatamente após o % especifica o comprimento total da saída formatada, possivelmente incluindo espaços. Esse número pode ser seguido por um período ( . ) e outro número, que especifica o número de posições decimais (dígitos à direita do ponto decimal) para exibir. Então, por exemplo, os comandos

printf "%12.2f\n" 1000000
printf "%12.2f\n" 1000
printf "%12.2f\n" 1
printf "%12.2f\n" 1.2345
printf "%12.2f\n" 1.6789
          ︙

produzirá a saída

  1000000.00
     1000.00
        1.00
        1.23
        1.68
         ︙

Observe que 1.6789 foi arredondado para 1.68 .

Portanto, você deve conseguir o resultado desejado com este comando:

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*//" |
     while IFS= read -r line
     do
         space="$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
         space_num=${space% *}
         space_mib=${space#* }
         printf "%-20s%12.2f %s\n" "$line" "$space_num" "$space_mib"
     done

O comando space=$(…) é apenas a substituição do comando port space que temos usado o tempo todo, mas com o resultado (que se parece com 0.088 MiB , por exemplo) atribuído a uma variável temporária, space . Então space_num=${space% *} define space_num para a parte desse antes do caractere de espaço (ou seja, o número; 0.088 neste exemplo), e space_mib=${space#* } conjuntos space_mib para a parte após o caractere de espaço (ou seja, as unidades, MiB ). Finalmente colamos todas as partes juntas usando printf %12.2f para exibir o número com duas casas decimais, arredondando para o centésimo mais próximo, e alinhando nos pontos decimais (como no exemplo anterior). Para seus dados, isso parece

mtr                         0.09 MiB
  pkgconfig                 0.62 MiB
    libiconv                6.27 MiB
      gperf                 0.00 MiB
  glib2                    46.09 MiB
    xz                      1.68 MiB
      gettext              24.83 MiB
        expat               1.11 MiB
        ncurses            15.17 MiB
    libxml2                10.40 MiB
      zlib                  0.72 MiB
    libffi                  0.14 MiB
    pcre                    5.95 MiB
      bzip2                 0.65 MiB
      libedit               0.80 MiB

Observe que 0.088 MiB foi arredondado para 0.09 MiB

Note também que space_mib é sempre definido como MiB , então, na verdade, não precisamos calculá-lo;

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*//" |
     while IFS= read -r line
     do
         space_num="$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1)"
         printf "%-20s%12.2f %s\n" "$line" "$space_num" "MiB"
     done

faz o mesmo que o anterior.

com truncamento

Para simplesmente truncar números, É melhor tratá-los como strings e não como números. Este comando

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*//" |
     while IFS= read -r line
     do
         printf "%-30s%s\n" "$line" \
                  "$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
     done | sed -E -e 's/(\..*) /0 /' -e 's/(.{25}) *(....\...).*( .*)//'

começa praticamente como a minha resposta anterior. Mas então canaliza tudo através de um sed , que tem duas partes:

  • s/(\..*) /0 /
    Isso corresponde ao ponto decimal ( \. ) e qualquer número de caracteres depois disso ( .* ) até, mas não incluindo, um caractere de espaço. Em seguida, ele substitui toda a string de correspondência com a parte antes do espaço ( ), dois zeros e um espaço. (Eu poderia ter dito s/(\..*)( )/0/ ; faria a mesma coisa.) Isso altera 0.088 MiB para 0.08800 MiB e 0.0 MiB para 0.000 MiB . Se você tivesse 42. MiB em seus dados, isso mudaria para 42.00 MiB . Mas note que ele assume que cada número tem um ponto decimal, mesmo que não tenha nenhum dígito depois disso. (Também assume que não há períodos nas strings mtr , pkgconfig , libiconv , etc.)

    Precisamos fazer isso para garantir que são pelo menos dois dígitos após o ponto decimal em cada número; esse não é o caso de gperf até fazermos essa correção.

  • s/(.{25}) *(....\...).*( .*)// e .{25} é a abreviação de ......................... ; isto é, 25 caracteres arbitrários. Presumo que seja suficientemente longo para capturar o mais longo (por exemplo, libiconv ), mais profundamente recuado, string. Então qualquer número de caracteres ( .* ), que, na verdade, espero ser apenas um monte de espaços. Então ....\... corresponde a quatro caracteres, um ponto decimal e mais dois personagens. Se você tiver um número de espaço maior que 9999 , você terá que mudar isso para coincidir com mais de quatro dígitos à esquerda do ponto decimal. Então qualquer número de caracteres ( .* ), que serão quaisquer dígitos após os dois primeiros após o ponto decimal. Então um espaço e o resto da linha ( ( .*) ), que eu espero que seja  MiB . Em seguida, coloca as peças de volta como a corda (com os espaços principais e seguintes apropriados), o número (com espaços iniciais suficientes para tornar o . o quinto caractere em ) e, em seguida, as unidades.

A saída deste comando se parece com

mtr                         0.08 MiB
  pkgconfig                 0.61 MiB
    libiconv                6.27 MiB
      gperf                 0.00 MiB
  glib2                    46.09 MiB
    xz                      1.67 MiB
      gettext              24.82 MiB
        expat               1.10 MiB
        ncurses            15.17 MiB
    libxml2                10.40 MiB
      zlib                  0.72 MiB
    libffi                  0.14 MiB
    pcre                    5.95 MiB
      bzip2                 0.64 MiB
      libedit               0.79 MiB

Observe que 0.088 MiB foi truncado para 0.08 MiB .

Claro que você pode colocar qualquer um dos comandos compostos acima em uma única linha se você quiser, e você deve ajustar as constantes de largura ( 12 , 20 , 25 , 30 , etc.) para atender às suas necessidades.

    
por 23.04.2016 / 07:33
0

Como você parece perceber, printf é uma ótima ferramenta para impressão formatada. Como você provavelmente aprendeu, %s é o formato para gerar uma string. %20s produz uma string de 20 caracteres, justificada à direita, preenchido com espaços à esquerda (se necessário). %-20s faz o oposto - gera uma string longa de 20 caracteres, à esquerda justificado, preenchido com espaços à direita (se necessário). Então, por exemplo, os comandos

printf "%-20s%s\n" "mtr" "0.088 MiB"
printf "%-20s%s\n" "  pkgconfig" "0.615 MiB"
printf "%-20s%s\n" "    libiconv" "6.270 MiB"
          ︙

produzirá a saída

mtr                 0.088 MiB
  pkgconfig         0.615 MiB
    libiconv        6.270 MiB
       ︙

Portanto, você deve conseguir o resultado desejado com este comando:

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*//" |
     while IFS= read -r line
     do
         printf "%-20s%s\n" "$line" \
                  "$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
     done

Costumo recomendar que você sempre cite as referências $ (por exemplo, variáveis) em comandos e scripts do shell a menos que você tenha uma boa razão para não. Observe que coloquei a expressão $(…) entre aspas. Mas, como $line contém espaços iniciais, é bem possível que

port space --units MiB --total "$line"

não funcionaria.

Claro que você pode colocar o comando composto acima em uma linha, se quiser e você deve ajustar o 20 às suas necessidades.

    
por 28.03.2016 / 05:11