grep pour les lignes qui ne suivent pas un motif

Aug 17 2020

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 historyutilisation 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 .bashrcj'avais la ligne suivante:

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

et comme j'utilise des multiplexeurs de terminaux ( screenet / ou tmux) la commande mentionnée ci-dessus est exécutée plusieurs fois (il en echo $PROMPT_COMMANDré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 history-issue (en changeant la commande en PROMPT_COMMAND="history -a; history -n) mais correction: cela n'a PAS résolu le problème avec les entrées dupliquées dans le 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

ilkkachu Aug 17 2020 at 03:48

Autant que je sache, cela grep -v "^#" -A 1signifie 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 grepest de se débarrasser des grep -Aimpressions de séparateurs de groupe .

Vous pouvez également uniq history.txttravailler pour imprimer une seule de chaque ensemble de lignes identiques consécutives.

jubilatious1 Aug 17 2020 at 18:40

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' fffopé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' fffopé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].

https://raku.org/