grep per le righe non dopo un pattern

Aug 17 2020

Sto cercando di trovare tutte le righe di un file che non seguono uno schema specifico.

Per qualche tempo ho avuto un problema con il mio historyutilizzo di GNU bash(versione 4 e 5) in cui i comandi apparivano in duplicati. Ho pensato che ciò fosse dovuto al fatto che nel mio .bashrcavevo la seguente riga:

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

e poiché sto usando multiplexer di terminale ( screene / o tmux) il comando sopra menzionato viene eseguito più volte (quindi echo $PROMPT_COMMANDrisulta inhistory -a; history -n; history -a; history -n;

In alcune situazioni (specialmente quando si fanno cose contemporaneamente su diversi pannelli / finestre / frame / buffer) l'ultimo comando che ho inserito è stato memorizzato due volte o anche più spesso nel mio file ~/.bash_history. Ciò ha portato a voci come le seguenti:

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

Inutile dire che questo è piuttosto fastidioso. Ho appena (si spera) trovato una soluzione per il historyproblema -issue (cambiando il comando in PROMPT_COMMAND="history -a; history -n) ma correzione: questo NON ha risolto il problema con voci duplicate nel file history.

Ora vorrei sbarazzarmi delle voci duplicate.

Pertanto sto attualmente cercando di trovare un'espressione regolare per contrassegnare tutto tranne le righe che iniziano con #e una riga dopo. La mia prima idea era combinare grep -v(per invertire la selezione) e grep -A 1(per ottenere una riga aggiuntiva dopo il modello di corrispondenza). Ma

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

non ha dato il risultato che speravo.

Quindi la mia domanda: qualcuno ha una buona idea su come farlo usando grep? Se no: come potrei fare questo con altri strumenti ( sed, awk, ...)?

Risposte

ilkkachu Aug 17 2020 at 03:48

Per quanto ho capito grep -v "^#" -A 1significa stampare le righe che non iniziano con un cancelletto e una riga dopo ciascuna. Ma non si vuole il contrario, stampare le righe che non iniziano con un cancelletto e una riga dopo?

Dato un file di prova:

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

grep -A1 ^# history.txt |grep -vxFe -- stampe:

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

Il secondo grepè eliminare le grep -Astampe dei separatori di gruppo .

In alternativa uniq history.txtdovrebbe funzionare per stampare solo una di ogni serie di righe identiche consecutive.

jubilatious1 Aug 17 2020 at 18:40

usando Raku (nata Perl6)

Sembra un lavoro per l'operatore "flip-flop", disponibile in numerosi linguaggi di scripting. Di seguito è una risposta utilizzando il linguaggio di programmazione Raku (precedentemente noto come Perl6). Innanzitutto inizia creando un file di test più ampio:

$ 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

Ora per una battuta che usa l' fffoperatore flip-flop di Raku , che implementa il comportamento "sed-like". L'acquisizione si attiva per le righe in cui la prima regex vede (all'inizio della riga ^^) un carattere "#" letterale. Una volta attivata, l'acquisizione ignora la prima regex e la valuta rispetto alla seconda, disattivando quando trova una corrispondenza con le righe in cui manca (all'inizio della riga ^^) un carattere "#". La regex "negativa" è implementata nel codice sottostante utilizzando <-[#]>, che è una "Classe di caratteri enumerata" negativa e una vera caratteristica del linguaggio 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

In realtà, la prima regex (a sinistra fffdell'operatore infisso) potrebbe essere scritta utilizzando <+[#]>una "Classe di caratteri enumerata" positiva, per una costruzione più parallela:

$ 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

Inoltre, mi sembra che tu possa migliorare la tua regex chiedendo una corrispondenza a favore o contro un "#" di inizio riga seguito da una o più cifre, ad esempio <digit>+, vedi sotto:

$ 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

[Tutto il codice sopra rimuove le righe duplicate che iniziano con B, D, E, G, H e I. L'unica stranezza che ho notato è che due righe di destinazione consecutive come "# 1596110297" appariranno nel tuo output, ma non è chiaro a me se il tuo file di input conterrà mai tali righe consecutive].

https://raku.org/