Come rimuovere parole particolari dalle righe di un file di testo?


13

il mio file di testo è simile al seguente:

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

ora voglio eliminare Liquid penetration 95% mass (m)dalle mie righe per ottenere solo i valori. Come dovrei farlo?


3
semplicementegrep -o '[^[:space:]]\+$' file
Avinash Raj,

@AvinashRaj: Al momento, questa soluzione ottiene la 'medaglia di stucco' :)
pa4080,

2
@ pa4080 Almeno per l'input che ho testato (linee 10M), l' approccio generale di Avinash Raj può essere reso un ordine di grandezza più veloce usando PCRE. (Potrei confermare che il motore, non lo schema, è responsabile, come GNU grep accetta \S+$con uno dei due -Eo -P.) Quindi questo tipo di soluzione non è intrinsecamente lento. Ma non riesco ancora a trovarlocut vicino al metodo di αғsнιη , che ha vinto anche il tuo benchmark .
Eliah Kagan,

Risposte:


22

Se c'è un solo =segno, puoi eliminare tutto prima e incluso in =questo modo:

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

Se si desidera modificare il file originale, utilizzare l' -iopzione dopo il test:

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

Appunti

  • -rusa ERE così non dobbiamo scappare (e)
  • s/old/newsostituire oldconnew
  • .* qualsiasi numero di qualsiasi personaggio
  • (things)risparmiare thingsper backreference più tardi con \1, \2ecc

Grazie ha funzionato. Ho usato questo comando per sovrascrivere il file esistente: sed -i -r 's /.*= (. *) / \ 1 /' time.txt Potresti spiegare come funziona?
OE,

Perché non evitare il backreference? s/^.*= //funzionerebbe ugualmente bene, poiché il valore corretto è alla fine della riga.
jpaugh,

@jpaugh Beh, in parte perché è troppo tardi per cambiare la mia risposta che è stata la prima pubblicata - altri hanno già dato la soluzione che menzioni e altri modi più efficienti per questo caso :) Ma forse mostrare come usare \1ecc ha un valore per le persone che atterrare su questa domanda durante la ricerca, che non hanno un problema così semplice
Zanna,

@Zanna È più generale, almeno.
jpaugh,

21

Questo è un lavoro per awk; supponendo che i valori si presentino solo nell'ultimo campo (come da esempio):

awk '{print $NF}' file.txt
  • NFè una awkvariabile, si espande al numero di campi in un record (riga), quindi $NF(notare il $davanti) contiene il valore dell'ultimo campo.

Esempio:

% 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

13

Ho deciso di confrontare le diverse soluzioni, elencate qui. A tale scopo ho creato un file di grandi dimensioni, basato sul contenuto fornito dall'OP:

  1. Ho creato un file semplice, chiamato 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. Quindi ho eseguito questo ciclo:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. La finestra del terminale è stata bloccata. Ho eseguito killall teeda un altro terminale. Quindi ho esaminato il contenuto del file con i comandi: less input.filee cat input.file. Sembrava buono, tranne l'ultima riga. Quindi ho rimosso l'ultima riga e creato una copia di backup: cp input.file{,.copy}(a causa dei comandi che utilizzano l' opzione sul posto).

  4. Il conteggio finale delle righe nel file input.fileè 2 192 473 . Ho ottenuto quel numero con il comando wc:

    $ cat input.file | wc -l
    2192473
    

Ecco il risultato del confronto:

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

    $ time grep -o '[^ [: space:]] \ + $' input.file> output.file
    
    0m58.539s reali
    utente 0m58.416s
    sys 0m0.108s
    
  • sed -ri 's/.* = (.*)/\1/'

    $ time sed -ri 's /.* = (. *) / \ 1 /' input.file
    
    0m26.936 reali
    utente 0m22.836s
    sys 0m4.092s
    

    In alternativa, se reindirizziamo l'output su un nuovo file, il comando è più veloce:

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

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

    $ time rev input.file | cut -d '' -f1 | rev> output.file
    
    0m3.703s reali
    utente 0m2.108s
    sys 0m4.916s
    
  • grep -oP '.*= \K.*'

    $ time grep -oP '. * = \ K. *' input.file> output.file
    
    0m3.328 reali
    utente 0m3.252s
    sys 0m0.072s
    
  • sed 's/.*= //' (rispettivamente l' -iopzione rende il comando alcune volte più lento)

    $ time sed 's /.*= //' input.file> output.file
    
    0m3.310s reali
    utente 0m3.212s
    sys 0m0.092s
    
  • perl -pe 's/.*= //' (qui l' -iopzione non produce grandi differenze nella produttività)

    $ time perl -i.bak -pe 's /.*= //' input.file
    
    0m3.187 reali
    utente 0m3.128s
    sys 0m0.056s
    
    $ time perl -pe 's /.*= //' input.file> output.file
    
    0m3.138s reali
    utente 0m3.036s
    sys 0m0.100s
    
  • awk '{print $NF}'

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

    $ time cut -c 35- input.file> output.file
    
    0m0.352s reali
    utente 0m0.284s
    sys 0m0.064s
    
  • cut -d= -f2

    $ time cut -d = -f2 input.file> output.file
    
    0m0.328 reali
    utente 0m0.260s
    sys 0m0.064s
    

La fonte dell'idea.


2
quindi la mia cut -d= -f2soluzione vince. haha
αғsнιη,

Puoi fornire ulteriori informazioni su come hai creato questo file? Inoltre, come vengono prodotti wc -ltre numeri? Quando non vengono passate altre opzioni, l' -lopzione dovrebbe sopprimere tutto tranne il conteggio delle righe.
Eliah Kagan,

@EliahKagan, fatto. Ho aggiornato la risposta.
pa4080

Ah, capisco, gli spazi erano separatori di gruppi di cifre. (Avevi wceffettivamente visualizzato quegli spazi? Ci sono impostazioni locali per le quali lo farà?) Grazie per l'aggiornamento!
Eliah Kagan,

@EliahKagan: Finalmente ho letto le tue domande wcancora una volta. Oggi non so dove fosse il mio ingegno, ma non riuscivo davvero a capirli. Quindi in effetti gli spazi erano separatori di gruppi di cifre e wcnon li aggiungono :)
pa4080,

12

Con grepe -Pper avere PCRE(interpretare il pattern come P erl- C ompatible R egular E xpression) e il -osolo modello di stampare abbinato. La \Knotifica ignorerà la parte corrispondente prima di se stessa.

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

Oppure puoi usare cutinvece command.

cut -d= -f2 infile

2
Oltre a eseguire il più veloce di tutti i metodi testati nel benchmark di pa4080 , il cutmetodo in questa risposta è stato anche il chiaro vincitore in un benchmark più piccolo che ho eseguito che ha testato meno metodi ma ha utilizzato un file di input più grande. Era ben oltre dieci volte più veloce della variante veloce del metodo che mi piace personalmente (e che la mia risposta riguarda principalmente).
Eliah Kagan,

11

Poiché il prefisso di linea ha sempre la stessa lunghezza (34 caratteri) puoi usare cut:

cut -c 35- < input.txt > output.txt

6

Invertire il contenuto del file con rev, reindirizzare l'output cutcon spazio come delimitatore e 1 come campo di destinazione, quindi invertire nuovamente per ottenere il numero originale:

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

5

Questo è semplice, breve e facile da scrivere, capire e controllare, e personalmente mi piace:

grep -oE '\S+$' file

grepin Ubuntu , quando viene invocato con -Eo -P, prende la scorciatoia \s per indicare un carattere di spazi bianchi (in pratica di solito uno spazio o una scheda) e \Sper indicare qualsiasi cosa che non sia uno. Usando il quantificatore+ e l'ancoraggio di fine linea$ , il modello \S+$abbina uno o più spazi non bianchi alla fine di una linea . Puoi usare -Pinvece di -E; il significato in questo caso è lo stesso ma viene utilizzato un motore di espressioni regolari diverso , quindi possono avere caratteristiche prestazionali diverse .

Ciò equivale alla soluzione commentata di Avinash Raj (solo con una sintassi più semplice e compatta):

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

Questi approcci non funzioneranno se dopo il numero potrebbero esserci spazi vuoti finali . Possono essere modificati così come fanno, ma non vedo alcun motivo di approfondire qui. Anche se a volte è istruttivo generalizzare una soluzione per lavorare in più casi, non è pratico farlo quasi tutte le volte che le persone tendono ad assumere, perché di solito non si ha modo di sapere in quali modi diversi incompatibili il problema potrebbe in definitiva avere bisogno essere generalizzato.


Le prestazioni sono a volte una considerazione importante. Questa domanda non stabilisce che l'input sia molto grande ed è probabile che ogni metodo che è stato pubblicato qui sia abbastanza veloce. Tuttavia, se si desidera la velocità, ecco un piccolo benchmark su un file di input di dieci milioni di righe:

$ 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

L'ho eseguito due volte nel caso in cui l'ordine fosse importante (come talvolta accade per le attività pesanti di I / O) e perché non avevo una macchina disponibile che non facesse altre cose in background che potrebbero distorcere i risultati. Da questi risultati, concludo quanto segue, almeno provvisoriamente e per i file di input delle dimensioni che ho usato:

  • Wow! Passaggio -P(per usare PCRE ) piuttosto che -G(impostazione predefinita quando non viene specificato alcun dialetto) o -Ereso greppiù veloce di un ordine di grandezza. Quindi, per file di grandi dimensioni, potrebbe essere meglio usare questo comando rispetto a quello mostrato sopra:

    grep -oP '\S+$' file
  • WOW!! Il cutmetodo nella risposta di αғsнιη , , è di oltre un ordine di grandezza più veloce rispetto anche la versione più veloce del mio modo! È stato anche il vincitore del benchmark di pa4080 , che ha coperto più metodi di questo ma con input più piccoli - ed è per questo che l'ho scelto, tra tutti gli altri metodi, da includere nel mio test. Se le prestazioni sono importanti o i file sono enormi, penso che dovrebbe essere usato il metodo αғsнιη .cut -d= -f2 filecut

    Questo serve anche a ricordare che la semplicità cute le pasteutilità non dovrebbero essere dimenticate e forse dovrebbero essere preferite quando applicabili, anche se ci sono strumenti più sofisticati come grepquelli spesso offerti come soluzioni di prima linea (e che io sono personalmente più abituato all'utilizzo).


4

perl- s ubstitute lo schema /.*= /con stringa vuota //:

perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
  • Da 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 - Sostituisci il motivo con una stringa vuota:

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

o (ma più lento di quanto sopra) :

sed -i.bak 's/.*= //' input.file
  • Cito questo approccio, perché è poche volte più veloce di quelli nella risposta di Zanna .

gawk- Sostituisci il motivo ".*= "con una stringa vuota "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • Da 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...
    
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.