grep para linhas não após um padrão
Estou tentando encontrar todas as linhas de um arquivo que não seguem um padrão específico.
Por algum tempo, tive um problema com meu history
uso do GNU bash
(versão 4 e 5), onde os comandos apareciam em duplicata. Presumi que isso se devia ao fato de que na minha .bashrc
eu tinha a seguinte linha:
PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
e como estou usando multiplexadores de terminal ( screen
e / ou tmux
) o comando mencionado acima é executado várias vezes (portanto, echo $PROMPT_COMMAND
resulta emhistory -a; history -n; history -a; history -n;
Em algumas situações (especialmente ao fazer coisas concomitantemente em painéis / janelas / frames / buffers diferentes), o último comando que digitei foi armazenado duas vezes ou até com mais frequência no meu ~/.bash_history
. Isso levou a entradas como as seguintes:
#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l
Não é preciso dizer que isso é muito chato. Eu apenas (espero) encontrei uma correção para o correção: isso NÃO resolveu o problema com entradas duplicadas no history
problema (alterando o comando para PROMPT_COMMAND="history -a; history -n
), mas history
.
Agora, gostaria de me livrar das entradas duplicadas.
Portanto, estou atualmente tentando encontrar uma expressão regular para marcar tudo, exceto as linhas que começam com #
e uma linha depois disso. Minha primeira ideia era combinar grep -v
(para inverter a seleção) e grep -A 1
(obter adicionalmente uma linha após o padrão de correspondência). Mas
grep -v "^#" -A 1 ~/.bash_history
não produziu o resultado que eu esperava.
Daí a minha dúvida: alguém tem uma boa ideia de como fazer isso usando grep
? Se não: como eu poderia fazer isso com outras ferramentas ( sed
, awk
...)?
Respostas
Pelo que entendi, grep -v "^#" -A 1
significa imprimir as linhas que não começam com uma cerquilha e uma linha após cada uma. Mas você não quer o oposto, imprimir as linhas que não começam com um sinal de hash, e uma linha depois?
Dado um arquivo de teste:
#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third
grep -A1 ^# history.txt |grep -vxFe --
estampas:
#123
echo this
#456
echo that
#789
echo third
A segunda grep
é livrar-se das grep -A
impressões dos separadores de grupo .
Alternativamente, uniq history.txt
deve trabalhar para imprimir apenas um de cada conjunto de linhas idênticas consecutivas.
usando Raku (née Perl6)
Isso parece um trabalho para o operador "flip-flop", disponível em várias linguagens de script. Abaixo está uma resposta usando a linguagem de programação Raku (anteriormente conhecida como Perl6). Comece criando um arquivo de teste mais extenso:
$ cat repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
B_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
D_yadm list -a | xargs -t ls -l
E_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
G_yadm list -a | xargs -t ls -l
H_yadm list -a | xargs -t ls -l
I_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Agora, para um one-liner usando o fff
operador flip-flop de Raku , que implementa um comportamento "semelhante ao sed". A captura é ativada para linhas onde a primeira regex vê (no início da linha ^^
) um caractere literal "#". Uma vez LIGADA, a captura ignora a primeira regex e avalia a segunda regex, desligando-se quando encontra uma correspondência nas linhas que estão faltando (no início da linha ^^
) um caractere "#". O regex 'negativo' é implementado no código abaixo usando <-[#]>
, que é uma "Classe de caractere enumerado" negativa e um recurso real da linguagem Raku:
$ raku -ne '.put if /^^ "#" / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Na verdade, a primeira regex (à esquerda do fff
operador infixo) poderia ser escrita usando <+[#]>
que é uma "Classe de caractere enumerado" positiva, para uma construção mais paralela:
$ raku -ne '.put if /^^ <+[#]> / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Além disso, parece-me que você pode melhorar sua regex exigindo uma correspondência a favor ou contra um "#" de início de linha seguido por um ou mais dígitos, ou seja <digit>+
, veja abaixo:
$ raku -ne '.put if /^^ <+[#]> <digit>+ / fff /^^ <-[#]> <-digit>+ /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
[Todo o código acima remove as linhas duplicadas começando com B, D, E, G, H e I. A única peculiaridade que notei são duas linhas de destino consecutivas como "# 1596110297" que aparecerão em sua saída, mas não está claro para mim se o seu arquivo de entrada irá conter tais linhas consecutivas].
https://raku.org/