Remover campos contendo string específica

3

Eu tenho file1 contendo vários campos separados por tabulações, nos quais eu gostaria de remover apenas os campos contendo uma string específica, no meu caso o caractere de sublinhado _ (não removendo toda a linha):

cat file1
357M        2054_
357_        154=        1900_
511_        419X        1481_        34=

Gostaria de obter o seguinte:

cat file2
357M
154=
419X        34=

Consegui remover os campos da seguinte forma:

cat file1 | perl -pe 's/\w+_\s*//g'
357M    154=        419X        34=

Mas o formato não é bom, porque eu gostaria de não alterar o número de colunas.

Eu também tentei:

cat file1 | sed 's/[0-9]*_//g'
357M
          154=
          419X         34=

Mas eu gostaria de me livrar dessas colunas vazias.

Uma abordagem de força bruta que também funciona:

cat file1 | sed 's/[0-9]*_//g' | tr -s '\t' '\t' | sed 's/^[ \t]*//g'
357M
154=
419X         34=

Este último comando: (1) remove todos os campos que contêm um sublinhado; (2) substitui várias guias em uma linha com apenas uma guia; (3) remove as guias principais. Não é tão elegante assim.

Alguma sugestão?

    
por aechchiki 30.08.2017 / 02:42

5 respostas

3

Você pode usar este simples sed .

sed 's/\w*_\s*//;/^$/d' infile.txt 

/^$/d excluirá as linhas vazias nas quais a linha inclui apenas um campo que termina com sublinhado foo_ ou _ sozinho.

Resultado:

357M
154=
419X    34=
    
por 30.08.2017 / 08:45
4

Considere:

sed 's/[^\t]*_//; s/\t[^\t]*_/\t/g' < input

Isso realiza duas substituições (condicionais):

  • o primeiro diz "qualquer caractere (zero ou mais) sem tabulação seguido por um sublinhado", substitua por "(nada)"
  • o segundo diz "substitua uma guia seguida por qualquer caractere (zero ou mais) que não seja tabulado seguido por um sublinhado" com "tab", e faça isso quantas vezes você encontrar esse padrão de pesquisa.

A primeira pesquisa é necessária para encontrar os campos principais que devem ser removidos; o segundo varre o resto.

Isso deixa os campos originais em suas colunas:

357M
        154=
        419X            34=

Para remover completamente os campos, basta incluir as guias no texto de pesquisa e substituição:

sed 's/[^\t]*_\t//; s/\t[^\t]*_//g' < input

Resultados em:

357M
154=
419X    34=
    
por 30.08.2017 / 03:25
2

Existe sempre a abordagem da "força bruta e ignorância".

  • Retire os campos inválidos
  • converta várias guias em uma única guia
  • Remover única guia da frente da linha
  • remover uma única guia do final da linha

Não é inteligente, não é inteligente, mas funciona.

A seguir, TAB significa o caractere TAB literal

sed -e 's/[0-9]*_//g' -e 's/TABTAB/TAB/g' -e 's/^TAB//' -e 's/TAB$//'

por exemplo

$ cat x
357M    2054_
357_    154=    1900_
511_    419X    1481_   34=
$ sed -e 's/[0-9]*_//g' -e 's/            /       /g' -e 's/^     //' -e 's/     $//' < x
357M
154=
419X    34=
    
por 30.08.2017 / 03:17
2

awk :

awk 'a=""; {for(i=1; i<=NF; ++i) {if($i ~ /[MX=]$/) a=(a?a"\t":"")$i}; \
     if(a) print a}' file.txt
  • a="" define a variável a como nulo para o registro atual, ou seja, tornando a específico do registro

  • for(i=1; i<=NF; ++i) {if($i ~ /[MX=]$/) a=(a?a"\t":"")$i} itera nos campos, verifica se o campo está terminando em M ou X ou = , se assim for, adiciona o campo à variável a com uma guia para separação entre qualquer salve previamente o campo

  • if(a) print a imprime a se não for nulo

Golfed:

awk 'a="";{for(i=1;i<=NF;++i)if($i~/[MX=]$/)a=(a?a"\t":"")$i;if(a)print a}'

Exemplo:

% cat file.txt                                                                    
357M        2054_
357_        154=        1900_
511_        419X        1481_        34=

% awk 'a=""; {for(i=1; i<=NF; ++i) {if($i ~ /[MX=]$/) a=(a?a"\t":"")$i};  if(a) print a}' file.txt
357M
154=
419X    34=
    
por 30.08.2017 / 03:33
1

Isso seria um pouco mais fácil se você estivesse preocupado apenas com os campos interiores (isto é, não o primeiro ou último campo em uma linha). Mas você quer olhar para todos os campos. Então eu tenho uma solução que faz parecer que não estamos lidando o último campo em cada linha:

sed -e 's/$/\t/' -e 's/[^\t]*_[^\t]*\t//g' -e 's/\t$//'

Isso

  1. Adiciona uma guia no final de cada linha (criando assim, com efeito, um campo n n + 1 , que é nulo).
  2. Localiza todos os campos (sequências de caracteres que não são guias) que contêm _ e os remove, e a seguinte guia, substituindo-os por nada. Isso funciona no campo n th (ou seja, o último campo na linha original) porque o passo 1 adicionou um separador no final.
  3. Remove a guia supérflua do final da linha.

Isso tem o recurso (que eu sei que você não pediu, mas você pode gostar de ver que ele está disponível) que preserva campos nulos:

$ cat file3
The             brown           jumps           the             dog.
        quick           fox             over            lazy
Four            and_            years
        score           seven           ago...

$ (the_above_command) file3
The             brown           jumps           the             dog.
        quick           fox             over            lazy
Four                    years
        score           seven           ago...

P.S. Dependendo de qual versão de sed você tem, talvez seja necessário digitar as guias reais no comando em vez de \t . Ou, se você estiver usando o bash, você pode usar $'…' para as sequências de comandos sed que contêm \t .

    
por 30.08.2017 / 03:51