Como extrair os números positivos na primeira coluna de uma saída como na pergunta?

2

Estou executando o Ubuntu 14.04.1 LTS de 64 bits com Bash 4.3.11(1)-release Tenho um programa chamado harminv que produz a saída da seguinte forma:

$ h5totxt hsli0.126.h5 | harminv -vt 0.1 -w 2-3 -a 0.9 -f 200 
# harminv: 1902 inputs, dt = 0.1
frequency, decay constant, Q, amplitude, phase, error
# searching frequency range 0.31831 - 0.477465
# using 200 spectral basis functions, density 6.60692
-2.14026, 3.511909e-05, 30471.5, 0.922444, 1.26783, 1.383955e-06
2.14013, 2.052504e-05, 52134.7, 0.920264, -1.27977, 3.426846e-07
# harminv: 2/6 modes are ok: errs <= 1.000000e-01 and inf * 3.426846e-07
, amps >= 0, 9.000000e-01 * 0.922444, |Q| >= 10

Quando a opção -v (verbose) é omitida, tenho uma saída muito mais nítida da seguinte forma:

$ h5totxt hsli0.126.h5 | harminv -t 0.1 -w 2-3 -a 0.9 -f 200 
frequency, decay constant, Q, amplitude, phase, error
-2.14026, 3.511909e-05, 30471.5, 0.922444, 1.26783, 1.383955e-06
2.14013, 2.052504e-05, 52134.7, 0.920264, -1.27977, 3.426846e-07

Gostaria de poder extrair os números positivos na primeira coluna da saída nos dois casos, mas não faço ideia de como fazê-lo, exceto que posso usar sed ou awk . Eu ficaria grato se alguém me apontar na direção certa, e meu objetivo é registrar cada número positivo para fazer um gráfico contra alguma outra variável.

    
por Vesnog 19.02.2015 / 22:56

2 respostas

2

Usando sed

Isso imprimirá apenas as linhas que começam com um número positivo:

sed -n 's/^\([[:digit:]][^ ,]*\).*//p'

Combinado com um dos seus pipelines, seria parecido com:

h5totxt hsli0.126.h5 | harminv -vt 0.1 -w 2-3 -a 0.9 -f 200 | sed -n 's/^\([[:digit:]][^ ,]*\).*//p'

Como funciona

  • -n

    Isso diz a sed para não imprimir nenhuma linha, a menos que nós explicitamente a solicitemos.

  • s/^\([[:digit:]][^ ,]*\).*//p

    Isso diz sed para procurar por linhas que começam com um número positivo e imprimir apenas esse número.

    Em uma expressão regular, ^ corresponde apenas no início de uma linha. [[:digit:]] corresponde a qualquer dígito. [^ ,]* corresponde a qualquer coisa que segue esse dígito, exceto um espaço ou uma vírgula. Tudo isso é agrupado com parênteses para que possamos nos referir ao número mais tarde como . A linha inteira é então substituída pelo número e, com a opção p , informamos sed para imprimi-lo.

    Um costumava usar [0-9] para corresponder aos dígitos. Com o advento das fontes unicode, isso não é mais confiável. A expressão [[:digit:]] , no entanto, é segura para unicode.

Alternativa usando regex estendido

Se você estiver usando o GNU sed (que é verdadeiro em todos os sistemas Linux), então a opção -r pode ser usada para obter expressões regulares estendidas . Com a regex estendida, os parens usados para agrupamento não precisam ser ignorados:

sed -rn 's/^([[:digit:]][^ ,]*).*//p'

No OSX ou outros sistemas BSD, use -E no lugar de -r .

Usando o awk

Isso faz o mesmo, mas usando awk :

awk -F, '/^[[:digit:]]/{print $1}'

Combinado com seu pipeline:

h5totxt hsli0.126.h5 | harminv -vt 0.1 -w 2-3 -a 0.9 -f 200 | awk -F, '/^[[:digit:]]/{print $1}'
    
por 19.02.2015 / 23:06
0

Dada a entrada que você mostra, o seguinte deve funcionar:

sed -n 's/[^[:digit:]]/\
&/;/.\n/P'

... ou ...

sed 's/[^[:digit:]].*//;/./!d'

... com alguns sed s, você também pode escrever como ...

sed -n 's/[^0-9]/\n&/;/.\n/P'

... ou ...

sed 's/[^0-9].*//;/./!d'

... e talvez até - dependendo do seu conjunto de dados de entrada - com o GNU sed , como ...

sed -n 's/\W/\n&/;/.\n/P'

... ou ...

sed 's/\W.*//;/./!d'

Como a linguagem regular é basicamente uma descrição dos complementos, você quase sempre pode transformar uma expressão regular em sua cabeça. Às vezes, faz menos trabalho quando você faz.

Então, se você está pesquisando o cabeçalho de uma string para um padrão específico que é de comprimento desconhecido, pode ser mais simples apenas procurar a primeira parte da string que não corresponde ao seu padrão.

O primeiro exemplo acima insere um caractere de% ewline \n antes do primeiro caractere não numérico encontrado em uma linha. Em seguida, verifica se, tendo feito isso (se o fez) , há pelo menos um caractere entre a inserção e o cabeçalho da linha. Caso contrário, ele não será impresso, mas, se for o caso, imprimirá somente até o \n ewline inserido.

O próximo exemplo é semelhante - apenas retira a string mais longa que pode de uma linha que começa com um caractere que não corresponde ao seu padrão, e exclui todas as linhas em branco da saída.

O resto é apenas shorthands para fazer mais do mesmo que alguns sed s podem interpretá-los, embora os dois primeiros sigam estritamente a especificação de sintaxe sed do POSIX (embora talvez [[:digit:]] seja um exagero , porque, pelo que entendi, o UTF-8 é um superconjunto ASCII e a maioria das linguagens que não incorporam algarismos arábicos também são diferentes o suficiente daquele em que escrevo para exigir outras modificações para tornar isso viável de qualquer maneira) .

Todos os exemplos - dependendo da implementação e entrada, conforme observado - devem imprimir apenas a primeira seqüência consecutiva de correspondências de dígitos que começam na linha de frente.

Pensando nisso, entretanto - já que parece que você está delimitando espaços e vírgulas de qualquer maneira - eu suponho que isso também poderia ser escrito:

sed -n 'y/, -/\n\n\n/;/^[0-9]/P'

... que dificilmente corresponde a qualquer expressão regular real - como a função y/// traduz caracteres em vez de agrupá-los em padrões. O matcher regexp é chamado apenas para testar o resultado.

    
por 20.02.2015 / 02:05