grep para líneas que no siguen un patrón

Aug 17 2020

Estoy tratando de encontrar todas las líneas de un archivo que no siguen un patrón específico.

Durante algún tiempo tuve un problema con el historyuso de GNU bash(versión 4 y 5) donde los comandos aparecían en duplicados. Supuse que esto se debía al hecho de que en mi .bashrctenía la siguiente línea:

 PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"

y como estoy usando multiplexores de terminal ( screeny / o tmux) el comando mencionado anteriormente se ejecuta varias veces (por lo tanto, echo $PROMPT_COMMANDda como resultadohistory -a; history -n; history -a; history -n;

En algunas situaciones (especialmente al hacer cosas simultáneamente en diferentes paneles / ventanas / marcos / búferes), el último comando que ingresé se almacenó dos veces o incluso más a menudo en mi ~/.bash_history. Esto llevó a entradas como las siguientes:

#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l

No hace falta decir que esto es bastante molesto. Acabo (con suerte) encontré una solución para el- historyproblema (cambiando el comando a PROMPT_COMMAND="history -a; history -n) pero corrección: esto NO resolvió el problema con entradas duplicadas en history.

Ahora me gustaría deshacerme de las entradas duplicadas.

Por lo tanto, actualmente estoy tratando de encontrar una expresión regular para marcar todo excepto las líneas que comienzan con #y una línea después de eso. Mi primera idea fue combinar grep -v(invertir la selección) y grep -A 1(obtener una línea adicional después del patrón coincidente). Pero

grep -v "^#" -A 1 ~/.bash_history

no dio el resultado que esperaba.

Por lo tanto, mi pregunta: ¿alguien tiene una buena idea sobre cómo hacer eso usando grep? Si no es así: ¿cómo podría lograr esto con otras herramientas ( sed, awk, ...)?

Respuestas

ilkkachu Aug 17 2020 at 03:48

Según tengo entendido, grep -v "^#" -A 1significa imprimir las líneas que no comienzan con un signo de almohadilla y una línea después de cada una. Pero no desea que el opuesto, imprimir las líneas que no empiezan con una almohadilla, y una línea después?

Dado un archivo de prueba:

#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third

grep -A1 ^# history.txt |grep -vxFe -- huellas dactilares:

#123
echo this
#456
echo that
#789
echo third

El segundo grepes deshacerse de las grep -Aimpresiones de los separadores de grupo .

Alternativamente, uniq history.txtdebería funcionar para imprimir solo uno de cada conjunto de líneas idénticas consecutivas.

jubilatious1 Aug 17 2020 at 18:40

usando Raku (de soltera Perl6)

Esto parece un trabajo para el operador "flip-flop", disponible en varios lenguajes de programación. A continuación se muestra una respuesta utilizando el lenguaje de programación Raku (anteriormente conocido como Perl6). Primero comience creando un archivo de prueba más 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

Ahora para una sola línea usando el fffoperador flip-flop de Raku , que implementa un comportamiento "similar a sed". La captura se activa para las líneas donde la primera expresión regular ve (al principio de la línea ^^) un carácter literal "#". Una vez activada, la captura ignora la primera expresión regular y se evalúa contra la segunda expresión regular, apagándose cuando encuentra una coincidencia con las líneas que faltan (al principio de la línea ^^) un carácter "#". La expresión regular 'negativa' se implementa en el siguiente código usando <-[#]>, que es una "Clase de caracteres enumerados" negativa y una característica real del lenguaje 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

En realidad, la primera expresión regular (a la izquierda del fffoperador infijo) podría escribirse usando <+[#]>una "Clase de caracteres enumerados" positiva, para una construcción más 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

Además, me parece que puede mejorar su expresión regular exigiendo una coincidencia a favor o en contra de un "#" de inicio de línea seguido de uno o más dígitos, es decir <digit>+, consulte a continuación:

$ 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 el código anterior elimina las líneas duplicadas que comienzan con B, D, E, G, H e I. La única peculiaridad que he notado es que aparecerán dos líneas de destino consecutivas como "# 1596110297" en el resultado, pero no está claro para mí si su archivo de entrada alguna vez contendrá tales líneas consecutivas].

https://raku.org/