grep -v: Como excluir apenas as primeiras (ou últimas) linhas N que correspondem?

7

Às vezes, há algumas linhas realmente irritantes em dados tabulares, como

column name | other column name
-------------------------------

Eu geralmente prefiro remover linhas de lixo que não deveriam estar lá por grep -v uma string razoavelmente única, mas o problema com essa abordagem é que se a string razoavelmente única aparece nos dados por acidente é um problema sério. / p>

Existe uma maneira de limitar o número de linhas que grep -v pode remover (digamos, 1)? Para pontos de bônus, existe uma maneira de contar o número de linhas do final sem recorrer a <some command> | tac | grep -v <some stuff> | tac ?

    
por Gregory Nisbet 06.01.2017 / 05:01

4 respostas

2

sed fornece uma maneira mais simples:

... |  sed '/some stuff/ {N; s/^.*\n//; :p; N; $q; bp}' | ...

Desta forma, você exclui a primeira ocorrência.

Se você quiser mais:

sed '1 {h; s/.*/iiii/; x}; /some stuff/ {x; s/^i//; x; td; b; :d; d}'

, em que contagem de i é a contagem de ocorrências (uma ou mais, não zero).

Explicação de várias linhas

sed '1 {
    # Save first line in hold buffer, put 'i's to main buffer, swap buffers
    h
    s/^.*$/iiii/
    x
}

# For regexp what we finding
/some stuff/ {
    # Remove one 'i' from hold buffer
    x
    s/i//
    x
    # If successful, there was 'i'. Jump to ':d', delete line
    td
    # If not, process next line (print others).
    b
    :d
    d
}'

Além disso

Provavelmente, essa variante funcionará mais rápido, porque ela lê todas as linhas de descanso e as imprime em uma única vez

sed '1 {h; s/.*/ii/; x}; /a/ {x; s/i//; x; td; :print_all; N; $q; bprint_all; :d; d}'

Como resultado

Você pode colocar este código no seu .bashrc (ou na configuração do seu shell, se for outro):

dtrash() {
    if [ $# -eq 0 ]
    then
        cat
    elif [ $# -eq 1 ]
    then
        sed "/$1/ {N; s/^.*\n//; :p; N; \$q; bp}"
    else
        count=""
        for i in $(seq $1)
        do
            count="${count}i"
        done
        sed "1 {h; s/.*/$count/; x}; /$2/ {x; s/i//; x; td; :print_all; N; \$q; bprint_all; :d; d}"

    fi
}

E use desta forma:

# Remove first occurrence
cat file | dtrash 'stuff' 
# Remove four occurrences
cat file | dtrash 4 'stuff'
# Don't modify
cat file | dtrash
    
por 06.01.2017 / 14:36
4

Você pode usar awk para ignorar as primeiras linhas n correspondentes (por exemplo, supondo que você queira remover apenas a primeira e a segunda correspondência do arquivo):

n=2
awk -v c=$n '/PATTERN/ && i++ < c {next};1' infile

Para ignorar as últimas linhas n que correspondem:

awk -v c=${lasttoprint} '!(/PATTERN/ && NR > c)' infile

em que ${lasttoprint} é o número da linha de n th + 1 para a última correspondência em seu arquivo. Existem várias maneiras de obter essa linha não. (por exemplo, imprima apenas o número da linha para cada correspondência por meio de ferramentas como sed / awk e, em seguida, tail | head para extraí-lo) ... aqui está uma maneira com gnu awk :

n=2
lasttoprint=$(gawk -v c=$((n+1)) '/PATTERN/{x[NR]};
END{asorti(x,z,"@ind_num_desc");{print z[c]}}' infile)
    
por 06.01.2017 / 19:13
0

Talvez reduza as chances de filtrar seus dados usando um comando grep mais preciso. Por exemplo:

grep -v -F -x 'str1'

Para linhas que são exatctly str1. Ou talvez:

grep -v '^str1.*str2$'

Para linhas que começam com 'str1' e terminam com 'str2'.

    
por 06.01.2017 / 16:38
0

Para fazer isso, você pode ter que usar o awk.

A maneira mais simples que conheço é esta:

cat file | awk '{ $1=""; print}'

Você também pode ignorar várias colunas:

cat file | awk '{ $1=$2=$3=""; print}'

Se você quiser pular a última coluna e não tiver certeza de quantas colunas terá:

cat file | awk '{ $NF=""; print}'

Testado no Ubuntu 16.04 (GNU bash, versão 4.3.48)

Melhor.

    
por 08.06.2018 / 09:44