grep pour les lignes qui ne suivent pas un motif
J'essaie de trouver toutes les lignes d'un fichier qui ne suivent pas un modèle spécifique.
Pendant un certain temps, j'ai eu un problème avec mon history
utilisation de GNU bash
(versions 4 et 5) où les commandes apparaissaient en double. J'ai supposé que cela était dû au fait que dans mon .bashrc
j'avais la ligne suivante:
PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
et comme j'utilise des multiplexeurs de terminaux ( screen
et / ou tmux
) la commande mentionnée ci-dessus est exécutée plusieurs fois (il en echo $PROMPT_COMMAND
résulte donchistory -a; history -n; history -a; history -n;
Dans certaines situations (en particulier lorsque vous effectuez des tâches simultanément sur différents volets / fenêtres / cadres / tampons), la dernière commande que j'ai entrée était stockée deux fois ou même plus souvent dans mon ~/.bash_history
. Cela a conduit à des entrées comme les suivantes:
#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l
Inutile de dire que c'est assez ennuyeux. J'ai juste (espérons-le) trouvé un correctif pour le correction: cela n'a PAS résolu le problème avec les entrées dupliquées dans le history
-issue (en changeant la commande en PROMPT_COMMAND="history -a; history -n
) mais history
.
Maintenant, je voudrais me débarrasser des entrées dupliquées.
Par conséquent, j'essaie actuellement de trouver une expression régulière pour tout marquer sauf les lignes commençant par #
et une ligne après. Ma première idée a été de combiner grep -v
(pour inverser la sélection) et grep -A 1
(pour obtenir en plus une ligne après le motif correspondant). Mais
grep -v "^#" -A 1 ~/.bash_history
n'a pas donné le résultat que j'espérais.
Par conséquent ma question: est-ce que quelqu'un a une bonne idée sur la façon de faire cela en utilisant grep
? Dans le cas contraire: comment pourrais - je accomplir cela avec d' autres outils ( sed
, awk
, ...)?
Réponses
Autant que je sache, cela grep -v "^#" -A 1
signifie imprimer les lignes qui ne commencent pas par un signe de hachage, et une ligne après chacune. Mais vous ne voulez pas le contraire, imprimer les lignes qui ne commencent par un signe dièse, et une ligne après?
Étant donné un fichier de test:
#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third
grep -A1 ^# history.txt |grep -vxFe --
imprime:
#123
echo this
#456
echo that
#789
echo third
La seconde grep
est de se débarrasser des grep -A
impressions de séparateurs de groupe .
Vous pouvez également uniq history.txt
travailler pour imprimer une seule de chaque ensemble de lignes identiques consécutives.
utilisant Raku (née Perl6)
Cela semble être un travail pour l'opérateur "flip-flop", disponible dans un certain nombre de langages de script. Voici une réponse utilisant le langage de programmation Raku (anciennement connu sous le nom de Perl6). Commencez par créer un fichier de test plus complet:
$ 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
Maintenant, pour un one-liner utilisant l' fff
opérateur flip-flop de Raku , qui implémente un comportement "sed-like". La capture s'active pour les lignes où la première expression régulière voit (au début de la ligne ^^
) un caractère littéral "#". Une fois activée, la capture ignore la première expression régulière et évalue par rapport à la seconde expression régulière, désactivant lorsqu'elle trouve une correspondance avec des lignes manquantes (en début de ligne ^^
) un caractère «#». L'expression régulière 'négative' est implémentée dans le code ci-dessous en utilisant <-[#]>
, qui est une "classe de caractères énumérés" négative et une caractéristique réelle du langage 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 fait, le premier regex (à gauche de l' fff
opérateur infixe) pourrait être écrit en utilisant <+[#]>
une "classe de caractères énumérés" positive, pour une construction plus parallèle:
$ 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
De plus, il me semble que vous pouvez améliorer votre expression régulière en exigeant une correspondance pour ou contre un début de ligne "#" suivi d'un ou plusieurs chiffres, c'est <digit>+
-à- dire voir ci-dessous:
$ 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
[Tout le code ci-dessus supprime les lignes dupliquées commençant par B, D, E, G, H et I. La seule bizarrerie que j'ai remarquée est que deux lignes cibles consécutives comme "# 1596110297" apparaîtront dans votre sortie, mais ce n'est pas clair à moi si votre fichier d'entrée contiendra jamais de telles lignes consécutives].