Como grep linhas que têm determinado valor em uma coluna específica?

9

Eu tenho um arquivo como o seguinte

  200.000    1.353    0.086
  200.250    1.417    0.000
  200.500    1.359    0.091
  200.750    1.423    0.000
  201.000    1.365    0.093
  201.250    1.427    0.000
  201.500    1.373    0.093
  201.750    1.432    0.000
  202.000    1.383    0.091
  202.250    1.435    0.000
  202.500    1.392    0.087
  202.750    1.436    0.000
  203.000    1.402    0.081
  203.250    1.437    0.001
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  

Gostaria de grep apenas as linhas que possuem na primeira coluna o decimal .000 e .500 somente para que a saída seja assim

  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  
Mohsen El-Tahawy
fonte
2
Parece fácil o suficiente. O que você tentou até agora? Quais problemas seu código teve?
John1024
talvez seja fácil para você, mas tentei com grep '.000' | grep '.005', mas também classifica as linhas que têm o mesmo valor em outras colunas
Mohsen El-Tahawy
3
Muito bom. As pessoas aqui são muito mais simpáticas se você mostrar uma tentativa honesta de resolver o problema. O código no seu comentário mostra isso. No futuro, se você incluir tentativas como essa em sua pergunta, provavelmente obterá respostas melhores com mais rapidez.
John1024

Respostas:

14

Você não usa grep. Use awk.

"your data" | awk '$1 ~ /\.[05]00/'
azzid
fonte
Muito bom. Conforme escrito, o código depende da existência exata de três dígitos após o decimal. Seria mais robusto de usar awk '$1 ~ /\.[05]0*$/'.
John1024
1
@ John1024, na verdade, como está escrito, o código depende de pelo menos três dígitos após o decimal. Eu me inclinaria a awk '$1 ~ /\.[05]00$/'mim mesmo (exigiria exatamente três dígitos), a menos que eu tivesse motivos para pensar que são esperadas casas decimais variáveis ​​na entrada.
Curinga
2
@Wildcard Se houver mais de três, o código poderá falhar. Por exemplo: echo 0.5001 | awk '$1 ~ /\.[05]00/'. Só funciona de forma confiável se houver exatamente três.
John1024
4
awk '$1 ~ /\.[50]00/ { print $0 }' myFile.txt

A primeira coluna $1será comparada com /\.500|\.000/os pontos e será escapada para ser pontos literais, sem regexar nenhum caractere, ~seja a correspondência parcial e imprima a linha inteira$0

Dalvenjia
fonte
2
Não há razão para incluir { print $0 }; essa é a ação padrão do Awk.
Curinga
4

Gostaria de saudar apenas as linhas que possuem na primeira coluna o decimal .000 e .500

Meu primeiro pensamento

grep '^ *[0-9][0-9][0-9]\.[50]00' filename

Teste rápido usando WSL

$ head testdata
              200.000    1.353    0.086
              200.250    1.417    0.000
              200.500    1.359    0.091
              200.750    1.423    0.000
              201.000    1.365    0.093
              201.250    1.427    0.000
              201.500    1.373    0.093
              201.750    1.432    0.000
              202.000    1.383    0.091
              202.250    1.435    0.000
$ grep '^ *[0-9][0-9][0-9]\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Existem maneiras mais concisas de expressar isso.

$ grep -E '^ *[0-9]{3}\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Se a primeira coluna tiver outra parte que não seja um número inteiro de três dígitos

grep -E '^ *[0-9]+\.[05]00' testdata

Sob algumas circunstâncias, pode ser necessário usá-lo [:digit:]no lugar de [0-9].

E assim por diante.

man grep é seu amigo.

RedGrittyBrick
fonte
Esse uso de grepé mais fácil de usar do que o meu. Eu não teria postado uma resposta, deveria ter visto isso primeiro. Bom trabalho!
Yokai
2

Dependendo do seu caso de uso, você também pode usar operações numéricas reais:

$ awk '{a = $1 % 1} a == 0 || a == 0.5' /tmp/foo
  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045

Testado com o BSD awk (OSX El Capitan, 20070501) e o GNU awk 4.1.4.

muru
fonte
1
Aviso: testar a igualdade exata do ponto flutuante (que o awk usa) geralmente fornece resultados "errados", a menos que os valores não tenham parte fracionária (e não sejam muito grandes em magnitude) ou a parte fracionária seja "binária" (exatamente metade, um trimestre, etc), o que é verdadeiro para os dados deste Q, mas não para muitos outros que parecem semelhantes aos não iniciados.
Dave_thompson_085
1
@ dave_thompson_085 de fato, mas com o gawk você pode usar aritmética de precisão arbitrária , é certo que não os estou usando aqui.
Muru
2
 grep -e '2[^ ]*.000' -e '2[^ ]*.500' file.txt
príncipe 987
fonte
2

Com awk:

$>awk '$1%.5==0' data.tsv 
200.000 1.353   0.086
200.500 1.359   0.091
201.000 1.365   0.093
201.500 1.373   0.093
202.000 1.383   0.091
202.500 1.392   0.087
203.000 1.402   0.081
203.500 1.412   0.073
204.000 1.423   0.065
204.500 1.432   0.055
205.000 1.441   0.045

Com mlr:

$>mlr --ifs tab --onidx filter '$1%.5==0' data.tsv 
200.000 1.353 0.086
200.500 1.359 0.091
201.000 1.365 0.093
201.500 1.373 0.093
202.000 1.383 0.091
202.500 1.392 0.087
203.000 1.402 0.081
203.500 1.412 0.073
204.000 1.423 0.065
204.500 1.432 0.055
205.000 1.441 0.045
FloHimself
fonte
2

Ok, um pouco tarde adicionando minha contribuição, mas acho que vale a pena.

O requisito para atender, de acordo com o OP, é a primeira coluna com o valor decimal de .000ou .500apenas. Não há estipulação quanto ao valor inicial, por faixa ou comprimento. Para robustez ele não deve ser considerado como estando restringido por qualquer coisa a não ser que não há nenhum caracter não-branco antes da primeira coluna (ou já não é a primeira coluna) e que o conteúdo da primeira coluna vai ter um ponto decimal, ., em algum lugar.

O OP está querendo usar grep, o que imprimirá a linha inteira quando uma correspondência for encontrada, portanto, a única coisa a fazer é criar o padrão que corresponda a tudo e somente ao necessário.

A própria simplicidade e nenhuma razão para usar sedou awkcomo `grep pode manipular a fonte como um arquivo ou um pipe.

Para grepum arquivo, usegrep '^[^.]*\.[05]0\{2\}\s' the_file.txt

Para greppartir de um tubo, usemy_command | grep '^[^.]*\.[05]0\{2\}\s'

O padrão é:, ^comece no início da linha; [^.], corresponda a qualquer caractere não decimal; *, tantas vezes quanto possível (incluindo nenhuma); \., corresponda a um ponto decimal; [05], corresponda a cinco ou a zero; 0\{2\}, combine mais 2 zeros (as barras invertidas antes da chave de abertura e fechamento impedem que o shell tente fazer a expansão da chave); \s, corresponda a um caractere de espaço em branco (ou seja, o final da coluna - para usar em um caso de uso diferente, substitua pelo separador de colunas, geralmente um comman, um ponto-e-vírgula ou uma guia \t).

Observe que isso corresponderá exatamente ao que o OP pediu. Ele não corresponderá .5000ou .0000mesmo que seja numericamente equivalente, porque o padrão procura cinco ou zero, seguido por exatamente mais 2 zeros seguidos por espaços em branco. Se isso for significativo, todas as outras respostas, até agora, falharão, pois corresponderão a qualquer número de zeros, maior que 1, após o dígito do teste. E, exceto na resposta do FloHimself, eles corresponderão a qualquer coisa na segunda coluna que comece .000 ou .500, incluindo .0003e .500T, e o do FloHimself corresponderá a qualquer coisa matematicamente equivalente a .0e.5, não importa quantos zeros existam. O último, embora não corresponda ao que o OP declarou, provavelmente corresponderá ao que o OP precisa de qualquer maneira.

Finalmente, se a potência e a velocidade de awksão desejadas, mesmo que o OP solicite grep, o comando seria:

Com um arquivo awk '$1 ~ /[^.]\.[05]0{2}$/' the_file.txt

Com um cano my_command | awk '$1 ~ /[^.]\.[05]0{2}$/'

user207673
fonte
1

Se você insiste em usar o grep, isso pode funcionar para você. Salvei a primeira saída fornecida em um arquivo de texto chamado "file.txt" e usei o seguinte comando:

grep -e '2[^ ]*.000' file.txt & grep -e '2[^ ]*.500' file.txt

O que fornece uma saída de:

200.000    1.353    0.086
200.500    1.359    0.091
201.500    1.373    0.093
201.000    1.365    0.093
202.500    1.392    0.087
202.000    1.383    0.091
203.500    1.412    0.073
203.000    1.402    0.081
204.500    1.432    0.055
204.000    1.423    0.065
205.000    1.441    0.045

Você não precisará salvar a saída em um arquivo de texto se ele já estiver em um arquivo. Mas, caso não esteja sendo salvo em um arquivo, você também pode canalizar os dados no comando grep que forneci e deve funcionar pelo menos até o primeiro número, pois 2a primeira coluna não é mais a 2. Nesse ponto, você precisará atualizar o comando grep com o caractere apropriado para imprimir corretamente.

O que está acontecendo com esse grepcomando duplo é que o primeiro grepestá sendo enviado ao plano de fundo com o &operador. Como é enviado para segundo plano, o próximo grepcomando é executado imediatamente depois, fornecendo uma saída uniforme. Para que a tarefa que você precisa concluir seja realizada com mais facilidade, siga o exemplo que outras pessoas deram e usam awkou até mesmo sed.

(editar)

Esse não é, de modo algum, o melhor ou mais eficaz uso do grep para suas necessidades, mas deve ser suficiente para você brincar um pouco e ter uma melhor noção do grep.

Yokai
fonte
O primeiro processo é executado em segundo plano, mas não é daemonizado, o que inclui a execução em segundo plano, mas um pouco mais. E é muito improvável que produza saída na mesma ordem que a entrada; mesmo em seu pequeno exemplo, já deu errado na terceira linha.
Dave_thompson_085
Ele não menciona que a saída precisa estar em uma ordem específica. Só que ele precisa ser específico para .500e .000da primeira coluna. Se precisar estar em uma ordem específica, como a menor para a maior, isso pode ser feito facilmente. No entanto, os três primeiros dígitos das primeiras colunas que estão sendo impressas estão na ordem mais baixa possível. Esse é o resultado de 2[^ ]*.000e 2[^ ]*.500. É bastante adequado ao que o OP pediu.
Yokai
Observe também minha edição para isenção de responsabilidade de eficiência para o comando que forneci.
Yokai