web-dev-qa-db-fra.com

Comment supprimer des mots particuliers des lignes d'un fichier texte?

mon fichier texte ressemble à ceci:

Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

maintenant, je veux supprimer Liquid penetration 95% mass (m) de mes lignes pour obtenir les valeurs uniquement. Comment devrais-je le faire?

13
O.E

S'il n'y a qu'un seul signe =, vous pouvez tout supprimer avant et y compris = comme ceci:

$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Si vous souhaitez modifier le fichier d'origine, utilisez l'option -i après le test:

sed -ri 's/.* = (.*)/\1/' file

Remarques

  • -r utilise ERE, nous n'avons donc pas à échapper à ( et à )
  • s/old/new remplace oldpar newname__
  • .* n'importe quel nombre de caractères
  • (things) save thingsto backreference ultérieurement avec \1, \2, etc.
22
Zanna

C'est un travail pour awk; en supposant que les valeurs apparaissent dans le dernier champ uniquement (selon votre exemple):

awk '{print $NF}' file.txt
  • NF est une variable awk, étend au nombre de champs dans un enregistrement (ligne), donc $NF (notez le $ devant) contient la valeur du dernier champ.

Exemple:

% cat temp.txt 
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
22
heemayl

Avec grep et le -P pour avoir PCRE (Interpréter le motif comme un P erl - C compatible R égal à E xpression) et le -o pour imprimer le motif correspondant uniquement. La notification \K ignorera la partie correspondante avant elle-même.

$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Ou vous pouvez utiliser la commande cut .

cut -d= -f2 infile
13
αғsнιη

J'ai décidé de comparer les différentes solutions énumérées ici. À cette fin, j'ai créé un fichier volumineux, basé sur le contenu fourni par l'OP:

  1. J'ai créé un fichier simple, nommé input.file:

    $ cat input.file
    Liquid penetration 95% mass (m) = 0.000205348
    Liquid penetration 95% mass (m) = 0.000265725
    Liquid penetration 95% mass (m) = 0.000322823
    Liquid penetration 95% mass (m) = 0.000376445
    Liquid penetration 95% mass (m) = 0.000425341
    
  2. Puis j'ai exécuté cette boucle:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. La fenêtre du terminal était bloquée. J'ai exécuté killall tee à partir d'un autre terminal. Ensuite, j'ai examiné le contenu du fichier à l'aide des commandes: less input.file et cat input.file. Cela avait l'air bien, sauf la dernière ligne. J'ai donc supprimé la dernière ligne et créé une copie de sauvegarde: cp input.file{,.copy} (à cause des commandes qui utilisent l'option inplace ).

  4. Le nombre final de lignes dans le fichier input.file est 2 192 473 . J'ai eu ce numéro avec la commande wc:

    $ cat input.file | wc -l
    2192473
    

Voici le résultat de la comparaison:

  • grep -o '[^[:space:]]\+$'

     $ time grep -o '[^ [: espace:]]\+ $' input.file> output.file 
     
     réel 0m58.539s 
     utilisateur 0m58.416s 
     Sys 0m0.108s 
    
  • sed -ri 's/.* = (.*)/\1/'

     $ time sed -ri '/.* = (. *)/\ 1 /' input.file 
     
     réel 0m26.936s 
     utilisateur 0m22. 836s 
     Sys 0m4.092s 
    

    Alternativement, si nous redirigeons la sortie vers un nouveau fichier, la commande est plus rapide:

     $ time sed -r 's /.* = (. *)/\ 1 /' input.file> output.file 
     
     Réel 0m19.734s 
     utilisateur 0m19,672s 
     sys 0m0.056s 
    
  • gawk '{gsub(".*= ", "");print}'

     $ time gawk '{gsub (". * =",) "; print}' input.file> output.file 
     
     réel 0m5.644s 
     utilisateur 0m5,568s 
     sys 0m0.072s 
    
  • rev | cut -d' ' -f1 | rev

     $ time rev input.file | cut -d '' -f1 | rev> fichier.sortie 
     
     réel 0m3.703s 
     utilisateur 0m2.108s 
     sys 0m4.916s 
    
  • grep -oP '.*= \K.*'

     $ time grep -oP '. * =\K. *' fichier.entrée> fichier.sortie 
     
     réel 0m3.328s 
     utilisateur 0m3.252s 
     sys 0m0.072s 
    
  • sed 's/.*= //' (respectivement l'option -i rend la commande plus lente)

     $ time sed '/.*= //' input.file> output.file 
     
     Réel 0m3.310s 
     Utilisateur 0m3.212s 
     sys 0m0.092s 
    
  • Perl -pe 's/.*= //' (l'option -i ne produit pas une grande différence de productivité ici)

     $ time Perl -i.bak -pe 's /.*= //' input.file 
     
     real 0m3.187s 
     utilisateur 0m3.128s 
     sys 0m0.056s 
    
     $ time Perl -pe '/.*= //' input.file> output.file 
     
     réel 0m3.138s 
     utilisateur 0m3.036s 
     sys 0m0.100s 
    
  • awk '{print $NF}'

     $ time awk '{print $ NF}' input.file> output.file 
     
     real 0m1.251s 
     utilisateur 0m1.164s 
     sys 0m0.084s 
    
  • cut -c 35-

     $ time cut -c 35- fichier.entrée> fichier.sortie 
     
     réel 0m0.352s 
     utilisateur 0m0.284s 
     sys 0m0. 064s 
    
  • cut -d= -f2

     $ time  coupe -d = -f2  input.file> output.file 
     
      réel 0m0.328s 
     utilisateur 0m0.260s 
     sys 0m0.064s 

La source de l'idée.

13
pa4080

Comme le préfixe de ligne a toujours la même longueur (34 caractères), vous pouvez utiliser cutname__:

cut -c 35- < input.txt > output.txt
11
David Foerster

Inversez le contenu du fichier avec rev, dirigez la sortie vers cut avec un espace comme délimiteur et 1 comme champ cible, puis inversez-le à nouveau pour obtenir le numéro d'origine:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
6
f1nan

C’est simple, court et facile à écrire, à comprendre et à vérifier, et j’aime bien, personnellement:

grep -oE '\S+$' file

grep dans Ubunt , lorsqu'il est appelé avec -E ou -P, prend le raccourci\s pour signifier un caractère d'espacement (en pratique généralement un espace ou une tabulation) et \S signifie tout ce qui n'en est pas un. En utilisant le quantificateur + et l'ancre de fin de ligne $ , , le modèle \S+$ correspond à un ou plusieurs éléments non vides à la fin d'une ligne . Vous pouvez utiliser -P au lieu de -E; la signification dans ce cas est la même, mais n moteur d'expressions régulières différent est utilisé. Par conséquent, peut avoir des caractéristiques de performance différentes .

Cela équivaut à la solution commentée d'Avinash Raj (avec une syntaxe plus simple et plus compacte):

grep -o '[^[:space:]]\+$' file

Ces approches ne fonctionneront pas s'il peut y avoir des espaces finaux après le nombre. Ils peuvent être modifiés comme ils le font, mais je ne vois aucune raison de les aborder ici. Bien qu'il soit parfois instructif de généraliser une solution afin de travailler dans plusieurs cas, il n'est pas pratique de le faire aussi souvent que les gens ont tendance à l'assumer, car on n'a généralement aucun moyen de savoir dans lequel de nombreux types différents sont incompatibles. façons le problème pourrait éventuellement devoir être généralisé.


La performance est parfois une considération importante. Cette question ne stipule pas que l'entrée est très importante et il est probable que chaque méthode publiée ici est suffisamment rapide. Toutefois, si la vitesse est souhaitée, voici un petit repère sur un fichier d'entrée de dix millions de lignes:

$ Perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M    bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135

Je l'ai exécuté deux fois au cas où l'ordre importait (comme c'est parfois le cas pour les tâches nécessitant beaucoup d'E/S) et parce que je n'avais pas de machine disponible qui ne faisait pas autre chose en arrière-plan qui pourrait fausser les résultats. Je conclus de ces résultats les conclusions suivantes, du moins provisoirement et pour les fichiers d'entrée de la taille que j'ai utilisée:

  • Hou la la! Passer -P (à utiliser PCRE ) plutôt que -G (valeur par défaut lorsque aucun dialecte n'est spécifié) ou -E a rendu grep plus rapide d'un ordre de grandeur supérieur. Donc, pour les gros fichiers, il peut être préférable d’utiliser cette commande plutôt que celle présentée ci-dessus:

    grep -oP '\S+$' file
  • WOW !! La méthode cut dans la réponse de αғsнιη , cut -d= -f2 file, est plus rapide que la version la plus rapide de mon chemin! C'était également le gagnant de référence de pa408 , qui couvrait plus de méthodes que cela, mais avec une entrée plus petite - et c'est pourquoi je l'ai choisie, parmi toutes les autres méthodes, à inclure dans mon test. Si les performances sont importantes ou si les fichiers sont énormes, je pense que la méthode cut de αғsнιη devrait être utilisée.

    Cela sert également à rappeler que le simple cut et paste utilitaires ne doit pas être oublié, et devrait peut-être être préféré le cas échéant, même s'il existe des outils plus sophistiqués comme grep qui sont souvent proposés en tant que solutions de première ligne (et auxquels je suis personnellement plus habitué en utilisant).

5
Eliah Kagan

Perl - s Remplacez le modèle /.*= / par une chaîne vide //:

Perl -pe 's/.*= //' input.file > output.file
Perl -i.bak -pe 's/.*= //' input.file
  • De Perl --help:

    -e program        one line of program (several -e's allowed, omit programfile)
    -p                assume loop like -n but print line also, like sed
    -i[extension]     edit <> files in place (makes backup if extension supplied)
    

sed - remplace le motif par une chaîne vide:

sed 's/.*= //' input.file > output.file

ou (mais plus lent que ce qui précède) :

sed -i.bak 's/.*= //' input.file
  • Je mentionne cette approche, car elle est quelques fois plus rapide que celle de Zanna réponse .

gawk - remplacez le modèle ".*= " par une chaîne vide "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • De man gawk:

    gsub(r, s [, t]) For each substring matching the regular expression r in the string t,
                     substitute the string s, and return the number of substitutions. 
                     If t is not supplied, use $0...
    
4
pa4080