grep para líneas que no siguen un patrón
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 history
uso 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 .bashrc
tenía la siguiente línea:
PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
y como estoy usando multiplexores de terminal ( screen
y / o tmux
) el comando mencionado anteriormente se ejecuta varias veces (por lo tanto, echo $PROMPT_COMMAND
da 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- corrección: esto NO resolvió el problema con entradas duplicadas en history
problema (cambiando el comando a PROMPT_COMMAND="history -a; history -n
) pero 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
Según tengo entendido, grep -v "^#" -A 1
significa 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 grep
es deshacerse de las grep -A
impresiones de los separadores de grupo .
Alternativamente, uniq history.txt
debería funcionar para imprimir solo uno de cada conjunto de líneas idénticas consecutivas.
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 fff
operador 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 fff
operador 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].