awk: Extraindo um número fixo de linhas onde o último número da linha pode variar

3

Esta pergunta pode parecer uma duplicata, mas apenas à primeira vista.
Naturalmente, eu não precisaria mais de ajuda para codificar um one-liner que extrai um número fixo de linhas contínuas (por exemplo, 5 neste exemplo) de uma fonte de dados, e. g. top :

$ top -b -n1 | awk 'BEGIN {printf "%23s %7s\n","cpu","mem"} NR==8,NR==12 {printf "%-16s %6s%% %6s%%\n",$12,$9,$10}'

Este é mesmo um one-liner muito útil que mostrará os processos no sistema que ocupam mais CPU, com o uso de memória sendo impresso em uma coluna adicional.

Até agora, tudo bem ... no entanto, não é tão trivial. Para obter essa lista, top é necessário e pode (em baixa carga do sistema) aparecer em si como processo nesta lista. Eu prefiro não querer isso, já que essas chamadas são feitas em intervalos e regularmente gerariam top (mesmo que por pouco tempo). Sabe-se que queremos começar na linha 8 (NR == 8). No entanto, e se um segundo top em outro desktop virtual fosse esquecido em um terminal que atrapalha a lista também? Nesse caso, dois top processos devem ser omitidos, portanto, a última linha a ser processada será 14.

Portanto, para melhorar essa saída e filtrar cada linha top que está lá, um contador parece obrigatório (talvez um loop for que saia com break ?). Infelizmente, minhas tentativas com um loop for e i = <number> foram infrutíferas até agora, porque seria melhor imprimir todas as linhas quantas vezes forem exibidas i .

Eu criei uma solução um tanto hackeada, que funciona, mas pode ser inadequada para casos mais complexos:

top -b -n1 | grep -v ' \btop\b$' | awk 'BEGIN {printf "%23s %7s\n","cpu","mem"} NR==8,NR==12 {printf "%-16s %6s%% %6s%%\n",$12,$9,$10}'

(Nota: Isso pode dar resultados indesejados se o nome de usuário na segunda coluna for "top" também)

De qualquer forma, eu poderia ter uma idéia de como fazer isso em awk (e se livrar do grep )?
Agradecemos antecipadamente.

    
por syntaxerror 10.09.2014 / 00:34

2 respostas

6

Isso não responde à sua pergunta,
mas resolve o problema que você está tentando resolver, de uma maneira completamente diferente:

O comando completo é este (veja abaixo, por exemplo, saída):

ps -o comm,%cpu,%mem --sort -%cpu -A | head -6

Vou descrever as partes:

  • usando ps para ter mais controle sobre a saída
  • Imprimindo apenas as três colunas que precisamos com -o comm,%cpu,%mem
  • Faça ps classificar os dados internamente --sort -%cpu , pela CPU, reversa.
  • Listar todos os processos com -A
  • Mostrar o cabeçalho e as 5 primeiras linhas do resultado por | head -6

A saída é semelhante à saída do seu primeiro comando:

$ ps -o comm,%cpu,%mem --sort -%cpu -A | head -6
COMMAND         %CPU %MEM
firefox          8.9 15.5
Xorg             1.3  5.6
parcellite       0.3  1.6
compiz           0.2  1.8
konsole          0.1  0.9

O processo ps está listado na lista completa - pode ser excluído com base no PID pai.

Se quisermos excluir top dos processos em outro lugar, podemos fazer isso com base no nome do comando.

O -A selecionando todos os processos seria substituído por -N ... :

ps ... -N --ppid $$ -C top

Como agora precisamos excluir processos, usamos -N para selecionar todos os outros processos do que os que combinamos.

Para excluir ps , usamos que ele tem o shell interativo atual como processo pai, então ele terá o pid pai, PPID do shell. O PID do shell atual é $$ .
Portanto, --ppid $$ corresponde a todos os processos filhos do shell atual e sabemos que haverá apenas um, ps .

Também queremos excluir os processos top que podem ser executados em outras exibições na mesma máquina. Fazemos isso combinando o nome do comando com -C top .

O comando completo com a exclusão do processo ps em si (e somente isso) e todos os processos top seria:

ps -o comm,%cpu,%mem --sort -%cpu -N --ppid $$ -C top | head -6
    
por 10.09.2014 / 01:28
5
top command | awk 'BEGIN { print header; count=5}
                   NR>=8 { if ($0 ~ /your top regex/) next;
                           print fields; if (--count == 0) exit}'

Em todas as linhas que começam com # 8, se corresponder a top , ignore-o. Caso contrário, imprima a parte desejada. Na quinta vez que você imprimir uma linha (que não corresponde a top ), saia.

    
por 10.09.2014 / 01:19