Como remover palavras específicas das linhas de um arquivo de texto?

10

meu arquivo de texto é assim:

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

agora quero excluir Liquid penetration 95% mass (m) das minhas linhas para obter apenas os valores. Como devo fazer isso?

    
por O.E 23.10.2017 / 12:02

8 respostas

22

Se houver apenas um sinal = , você poderá excluir tudo antes e incluindo = da seguinte forma:

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

Se você quiser alterar o arquivo original, use a opção -i após o teste:

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

Notas

  • -r usa ERE, por isso não precisamos escapar de ( e )
  • s/old/new replace old com new
  • .* qualquer número de caracteres
  • (things) save things para referência posterior com , , etc.
por Zanna 23.10.2017 / 12:06
21

Este é um trabalho para awk ; assumindo que os valores ocorrem apenas no último campo (conforme o seu exemplo):

awk '{print $NF}' file.txt
  • NF é uma variável awk , expande para o número de campos em um registro (linha), portanto $NF (observe o $ na frente) contém o valor do último campo.

Exemplo:

% 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
    
por heemayl 23.10.2017 / 14:03
13

Eu decidi comparar as diferentes soluções, listadas aqui. Para este propósito eu criei um arquivo grande, baseado no conteúdo fornecido pelo OP:

  1. Eu criei um arquivo simples chamado 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. Então eu executei este loop:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. A janela do terminal foi bloqueada. Eu executei killall tee de outro terminal. Em seguida, examinei o conteúdo do arquivo pelos comandos: less input.file e cat input.file . Parecia bom, exceto a última linha. Então eu removi a última linha e criei uma cópia de backup: cp input.file{,.copy} (por causa dos comandos que usam a opção inplace ).

  4. A contagem final das linhas no arquivo input.file é 2 192 473 . Eu obtive esse número pelo comando wc :

    $ cat input.file | wc -l
    2192473
    

Aqui está o resultado da comparação:

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

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

    $ time sed -ri 's/.* = (.*)//' input.file
    
    real    0m26.936s
    user    0m22.836s
    sys     0m4.092s
    

    Como alternativa, se redirecionarmos a saída para um novo arquivo, o comando será mais rápido:

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

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

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

    $ time grep -oP '.*= \K.*' input.file > output.file
    
    real    0m3.328s
    user    0m3.252s
    sys     0m0.072s
    
  • sed 's/.*= //' (respectivamente, a opção -i faz o comando algumas vezes mais devagar)

    $ time sed 's/.*= //' input.file > output.file
    
    real    0m3.310s
    user    0m3.212s
    sys     0m0.092s
    
  • perl -pe 's/.*= //' (a opção -i não produz grande diferença na produtividade aqui)

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

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

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

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

A fonte da ideia.

    
por pa4080 24.10.2017 / 18:56
12

Com grep e o -P por ter PCRE (interprete o padrão como um P erl- < strong> C ompatible R egular E ) e -o para imprimir o padrão correspondente sozinho. O \K notify irá ignorar a parte correspondente antes de si.

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

Ou você pode usar o comando cut .

cut -d= -f2 infile
    
por αғsнιη 23.10.2017 / 14:21
11

Como o prefixo de linha sempre tem o mesmo tamanho (34 caracteres), você pode usar cut :

cut -c 35- < input.txt > output.txt
    
por David Foerster 23.10.2017 / 16:56
6

Inverta o conteúdo do arquivo com rev , canalize a saída em cut com espaço como delimitador e 1 como o campo de destino e, em seguida, inverta-o novamente para obter o número original:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
    
por f1nan 23.10.2017 / 18:15
5

Isso é simples, curto e fácil de escrever, entender e verificar, e eu pessoalmente gosto disso:

grep -oE '\S+$' file

grep no Ubuntu , quando invocado com -E ou -P , recebe o < uma abreviação \s para significar um espaço em branco (na prática geralmente um espaço ou tab) e \S para significar qualquer coisa que não seja um. Usando o o quantificador + e a âncora de final de linha $ , o padrão \S+$ corresponde a um ou mais não-brancos no final de uma linha . Você pode usar -P em vez de -E ; o significado neste caso é o mesmo, mas um mecanismo diferente de expressões regulares é usado, então eles pode ter diferentes características de desempenho .

Isso é equivalente a Avinash Solução comentada de Raj (apenas com uma sintaxe mais fácil e mais compacta):

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

Essas abordagens não funcionarão se houver espaço em branco após o número. Elas podem ser modificadas, mas não vejo sentido em entrar aqui. Embora às vezes seja instrutivo generalizar uma solução para trabalhar em mais casos, não é prático fazer isso com tanta frequência quanto as pessoas tendem a supor, porque geralmente não há como saber em quais das diferentes maneiras incompatíveis o problema pode acabar por ser generalizado.

O desempenho às vezes é uma consideração importante. Esta pergunta não estipula que a entrada é muito grande e é provável que todos os métodos publicados aqui sejam rápidos o suficiente. No entanto, caso a velocidade seja desejada, aqui está uma pequena referência em um arquivo de entrada de dez milhões de linhas:

$ 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

Eu o executei duas vezes no caso de o pedido ser importante (como às vezes acontece em tarefas pesadas de E / S) e porque não tinha uma máquina disponível que não estivesse fazendo outras coisas em segundo plano que pudessem distorcer os resultados . A partir desses resultados concluo o seguinte, pelo menos provisoriamente, e para arquivos de entrada do tamanho que usei:

  • Uau! Passando -P (para usar PCRE ) em vez de -G (o padrão quando nenhum dialeto é especificado) ou -E fez grep mais rápido em uma ordem de grandeza. Portanto, para arquivos grandes, pode ser melhor usar esse comando do que o mostrado acima:

    grep -oP '\S+$' file
  • WOW !! O método cut em resposta da αғsнιη , cut -d= -f2 file , é mais de uma ordem de magnitude mais rápida do que a versão mais rápida do meu caminho! Foi o vencedor no benchmark do pa4080 também, que cobriu mais métodos do que isso, mas com menor participação - e é por isso que Eu escolhi, de todos os outros métodos, para incluir no meu teste. Se o desempenho é importante ou os arquivos são enormes, eu acho que o método cut deve ser usado.

    Isso também serve como um lembrete de que o simples cut e paste utilitários não deve ser esquecido , e talvez deva ser preferido quando aplicável, mesmo que existam ferramentas mais sofisticadas como grep que geralmente são oferecidos como soluções de primeira linha (e que eu pessoalmente estou mais acostumado a usar) .

por Eliah Kagan 24.10.2017 / 23:15
4

perl - s ubstitua o padrão /.*= / com a string vazia // :

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 - substitua o padrão por string vazia:

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

ou (mas mais lento que o anterior) :

sed -i.bak 's/.*= //' input.file
  • Eu menciono essa abordagem, porque ela é algumas vezes mais rápida do que as da resposta da Zanna.

gawk - substitua o padrão ".*= " pela string vazia "" :

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...
    
por pa4080 24.10.2017 / 10:02