Comment grep des lignes qui ont une certaine valeur dans une colonne spécifique?

9

J'ai un fichier comme suit

  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  

Je voudrais ne grep que les lignes qui ont dans la première colonne les décimaux .000 et .500 uniquement pour que la sortie soit comme ceci

  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
la source
2
Cela semble assez facile. Qu'est-ce que tu as essayé jusque-là? Quels problèmes votre code a-t-il rencontrés?
John1024
c'est peut-être facile pour vous mais j'ai essayé avec grep '.000' | grep '.005' mais il trie également les lignes qui ont la même valeur dans les autres colonnes
Mohsen El-Tahawy
3
Très bien. Les gens ici sont beaucoup plus sympathiques si vous montrez une tentative honnête de résoudre le problème vous-même. Le code dans votre commentaire le montre. À l'avenir, si vous incluez de telles tentatives dans votre question, vous obtiendrez probablement de meilleures réponses plus rapidement.
John1024

Réponses:

14

Vous n'utilisez pas grep. Utilisez awk.

"your data" | awk '$1 ~ /\.[05]00/'
azzid
la source
Très bien. Tel qu'il est écrit, le code dépend de la présence d'exactement trois chiffres après la décimale. Il serait plus robuste à utiliser awk '$1 ~ /\.[05]0*$/'.
John1024
1
@ John1024, en fait, tel qu'il est écrit, le code dépend de la présence d' au moins trois chiffres après la décimale. J'inclinerais awk '$1 ~ /\.[05]00$/'moi-même (il faut exactement trois chiffres), à moins d'avoir des raisons de penser que des décimales variables sont attendues dans l'entrée.
Wildcard
2
@Wildcard S'il y en a plus de trois, le code peut échouer. Par exemple: echo 0.5001 | awk '$1 ~ /\.[05]00/'. Cela ne fonctionne de manière fiable que s'il y en a exactement trois.
John1024
4
awk '$1 ~ /\.[50]00/ { print $0 }' myFile.txt

La première colonne $1sera comparée /\.500|\.000/aux points sont échappés pour être des points littéraux ne pas regex aucun caractère la ~correspondance partielle, et imprimer la ligne entière$0

Dalvenjia
la source
2
Aucune raison d'inclure { print $0 }; c'est l'action par défaut d'Awk.
Wildcard
4

Je voudrais ne grep que les lignes qui ont dans la première colonne les décimaux .000 et .500

Ma première pensée

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

Test rapide à l'aide de 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

Il existe des moyens plus concis d'exprimer cela.

$ 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

Si la première colonne peut comporter une partie entière à 3 chiffres

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

Dans certaines circonstances, vous devrez peut-être utiliser [:digit:]à la place de [0-9].

Etc.

man grep est votre ami.

RedGrittyBrick
la source
Cette utilisation de grepest plus facile à utiliser que la mienne. Je n'aurais pas posté de réponse si j'avais d'abord vu cela. Bon travail!
Yokai
2

Selon votre cas d'utilisation, vous pouvez également utiliser des opérations numériques réelles:

$ 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

Testé avec BSD awk (OSX El Capitan, 20070501) et GNU awk 4.1.4.

muru
la source
1
Avertissement: tester l'égalité exacte de virgule flottante (que awk utilise) donne souvent des résultats «incorrects» sauf si les valeurs n'ont pas de partie fractionnaire (et ne sont pas trop importantes) ou si la partie fractionnaire est «binaire» (exactement la moitié, un trimestre, etc.) ce qui est vrai pour les données de ce Q mais pas beaucoup d'autres qui semblent similaires aux non-initiés.
dave_thompson_085
1
@ dave_thompson_085 en effet, mais avec gawk vous pouvez utiliser l'arithmétique de précision arbitraire , certes je ne les utilise pas ici.
muru
2
 grep -e '2[^ ]*.000' -e '2[^ ]*.500' file.txt
prince 987
la source
2

Avec 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

Avec 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
la source
2

D'accord, un peu tard pour ajouter ma contribution, mais je pense que ça vaut le coup.

L'obligation de satisfaire, selon l'OP, est la première colonne ayant la valeur décimale de .000ou .500seulement. Il n'y a aucune stipulation quant à la valeur principale, ni par plage ni par longueur. Pour la robustesse , il ne faut pas présumer être contraint par quoi que ce soit , sauf qu'il n'y a pas de caractères non-vides avant la première colonne (ou il est plus la première colonne) et que le contenu de la première colonne sera un point décimal, ., quelque part.

L'OP souhaite utiliser grep, ce qui imprimera la ligne entière lorsqu'une correspondance est trouvée, donc la seule chose à faire est de créer le modèle qui correspond à tout et seulement ce qui est requis.

La simplicité elle-même, et aucune raison d'utiliser sedou awkcomme `grep peut gérer la source comme un fichier ou un canal.

Pour greputiliser un fichiergrep '^[^.]*\.[05]0\{2\}\s' the_file.txt

À greppartir d'un tuyau, utilisezmy_command | grep '^[^.]*\.[05]0\{2\}\s'

Le modèle est:, ^commencez au début de la ligne; [^.], correspond à tout caractère non décimal; *, autant de fois que possible (y compris aucune); \., correspond à un point décimal; [05], correspond à un cinq ou à un zéro; 0\{2\}, correspond à 2 autres zéros (les barres obliques inverses avant l'accolade ouverte et fermée empêchent le shell d'essayer de faire l'expansion de l'accolade); \s, correspond à un espace (signifiant la fin de la colonne - à utiliser dans un autre cas d'utilisation, remplacez-le par le séparateur de colonne, généralement une virgule, un point-virgule ou une tabulation \t).

Notez que cela correspondra exactement à ce que l'OP a demandé. Il ne correspondra pas.5000 ou .0000même s'il est numériquement équivalent, car le motif recherche un cinq ou un zéro, suivi d'exactement 2 autres zéros suivis d'un espace. Si cela est significatif, toutes les autres réponses, jusqu'à présent, échouent en ce sens qu'elles correspondront à n'importe quel nombre de zéros, supérieur à 1, après le chiffre de test. Et sauf à la réponse de FloHimself, ils correspondront à tout dans la deuxième colonne qui commence .000 ou .500, y compris .0003et .500T, et celui de FloHimself correspondra à tout ce qui est mathématiquement équivalent à .0et.5, peu importe le nombre de zéros. Le dernier, tout en ne correspondant pas à ce que le PO a déclaré, est susceptible de correspondre de toute façon à ce dont le PO a besoin.

Enfin, si la puissance et la vitesse de awksont souhaitées, même si l'OP le demandait grep, alors la commande serait:

Avec un fichier awk '$1 ~ /[^.]\.[05]0{2}$/' the_file.txt

Avec une pipe my_command | awk '$1 ~ /[^.]\.[05]0{2}$/'

user207673
la source
1

Si vous insistez pour utiliser grep, cela peut fonctionner pour vous. J'ai enregistré la première sortie que vous fournissez dans un fichier texte appelé "file.txt", puis j'ai utilisé la commande suivante:

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

Ce qui donne une sortie 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

Vous n'aurez pas à enregistrer la sortie dans un fichier texte s'il est déjà dans un fichier. Mais au cas où il ne serait pas enregistré dans un fichier, vous pouvez également diriger les données dans la commande grep que j'ai fournie et cela devrait fonctionner au moins jusqu'au premier chiffre 2, dans la première colonne n'est plus un 2. À ce stade, vous devrez mettre à jour la commande grep avec le caractère approprié pour imprimer correctement.

Ce qui se passe avec cette double grepcommande, c'est que la première grepest envoyée en arrière-plan avec l' &opérateur. Lorsqu'elle est envoyée en arrière-plan, la grepcommande suivante s'exécute immédiatement après pour vous donner une sortie uniforme. Pour que la tâche à accomplir soit plus facile à réaliser, vous devez suivre l'exemple que d'autres ont donné et utiliser awkou même sed.

(Éditer)

Ce n'est en aucun cas l'utilisation la meilleure ou la plus efficace de grep pour vos besoins, mais cela devrait être suffisant pour que vous puissiez jouer un peu et avoir une meilleure idée de grep.

Yokai
la source
Le premier processus s'exécute en arrière-plan, mais n'est pas démonifié, ce qui inclut l' exécution en arrière-plan mais un peu plus. Et il est très peu probable de produire une sortie dans le même ordre que l'entrée; même dans votre tout petit exemple, il a déjà mal tourné à la troisième ligne.
dave_thompson_085
Il ne mentionne pas que la sortie doit être dans un ordre spécifique. Seulement, il doit être spécifique au .500et .000de la première colonne. Si elle doit être dans un ordre spécifique, du moins au plus grand, cela peut facilement être fait. Cependant, les 3 premiers chiffres des premières colonnes imprimées sont au moins dans l'ordre le plus élevé. C'est le résultat du 2[^ ]*.000et 2[^ ]*.500. C'est tout à fait conforme à ce que le PO a demandé.
Yokai
Notez également mon avertissement relatif à l'efficacité pour la commande que j'ai fournie.
Yokai