sed o awk: elimina n righe che seguono uno schema


105

Come potrei mescolare pattern e intervalli numerici in sed (o qualsiasi strumento simile, ad esempio awk)? Quello che voglio fare è abbinare determinate righe in un file ed eliminare le successive n righe prima di procedere, e voglio farlo come parte di una pipeline.

Risposte:


187

Ci proverò.

Per eliminare 5 righe dopo un motivo (inclusa la riga con il motivo):

sed -e '/pattern/,+5d' file.txt

Per eliminare 5 righe dopo un motivo (esclusa la riga con il motivo):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt

14
Notare che il +Npattern è un'estensione GNU. Cambia il primo nin un Nnel secondo esempio per includere la linea con il motivo.
In pausa fino a nuovo avviso.

2
come eliminare tutte le righe dopo che il modello è abbinato? Sto usando sed -e '/ <! - # content end -> </div> /, $ d' out.txt ma dà errore dicendo: sed: -e espressione # 1, char 24: caratteri extra dopo comando Grazie in anticipo.
N mol

8
Quello che sta succedendo è simile ma leggermente diverso in ogni caso. Nella prima ricetta, /pattern/,+5definisce un intervallo, che inizia con una riga contenente "pattern" ( /pattern/) e termina 5 righe dopo ( +5). L'ultimo carattere dè un comando da eseguire su ogni riga in quell'intervallo, che è "cancella". Nella seconda ricetta, invece di abbinare un intervallo, corrisponde solo alla riga contenente il pattern ( /pattern/) e quindi esegue una serie di comandi {n;N;N;N;N;d}:, che fondamentalmente stampa la riga successiva ( n) e quindi legge e infine scarta le successive 4 righe ( N;N;N;N;d).
pimlottc

18
Su sistemi Mac / OS X è necessario aggiungere un punto e virgola prima della parentesi di chiusura:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL

1
Per completezza: per eliminare tutte le righe che seguono un certo schema, something fare :,sed -E '/^something$/,$d' dove si -Etrova la regex estesa di portabilità POSIX.
not2qubit

7

Senza estensioni GNU (ad esempio su macOS):

Per eliminare 5 righe dopo un motivo (inclusa la riga con il motivo)

 sed -e '/pattern/{N;N;N;N;d;}'

Aggiungi -i ''per modificare sul posto.


6

awkSoluzioni semplici :

Supponiamo che l'espressione regolare da utilizzare per trovare le righe corrispondenti sia memorizzata nella variabile di shell $regexe il conteggio delle righe da saltare $count.

Se la linea di corrispondenza deve anche essere saltato ( $count + 1linee sono saltati):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Se la riga corrispondente non deve essere saltata (le $countrighe dopo la corrispondenza vengono saltate):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Spiegazione:

  • -v regex="$regex" -v count="$count"definisce awkvariabili basate su variabili di shell con lo stesso nome.
  • $0 ~ regex corrisponde alla linea di interesse
    • { skip=count; next }inizializza il conteggio dei salti e procede alla riga successiva, saltando effettivamente la riga corrispondente; nel 2 ° soluzione, le printprima nextgarantisce che è non saltati.
    • --skip >= 0 decrementa il conteggio dei salti e agisce se è (ancora)> = 0, il che implica che la riga in questione dovrebbe essere saltata.
    • { next } procede alla riga successiva, saltando effettivamente la riga corrente
  • 1è una scorciatoia comunemente usata per { print }; cioè, la riga corrente viene semplicemente stampata
    • Solo le righe non corrispondenti e non ignorate raggiungono questo comando.
    • Il motivo che 1è equivalente a { print }è che 1viene interpretato come un modello booleano che per definizione restituisce sempre true, il che significa che la sua azione associata (blocco) viene eseguita incondizionatamente. Poiché in questo caso non è presente alcuna azione associata, il awkvalore predefinito è stampare la riga.

3

Questo potrebbe funzionare per te:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21

10
Wow, è criptico.
pimlottc

3
Una soluzione intelligente (sebbene specifica per GNU-Sed), ma poche persone ne trarranno beneficio, a meno che non si aggiunga una spiegazione. pattern_number.txtè un file a 2 colonne contenente il modello da abbinare nella prima colonna e nella seconda il numero di righe da saltare. Il primo sedcomando trasforma il file in uno sedscript che esegue la corrispondenza e il salto corrispondenti; quello script è fornito tramite -fe stdin ( -) al secondo sedcomando. Il secondo sedcomando opera su un file di input ad-hoc di esempio formato dall'output di seq 21per dimostrare che funziona.
mklement0

Inoltre, la soluzione viene fornita con un avvertimento: il metodo che utilizza per non saltare la prima riga (quella che corrisponde al modello) ha l'effetto collaterale di non saltare anche le righe duplicate nell'intervallo.
mklement0

Questo è un uso impressionante di sed.
Travis Rodman

3

Utilizzando Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$

2

Questa soluzione ti consente di passare "n" come parametro e leggerà i tuoi modelli da un file:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

Il file chiamato "-" significa stdin per awk, quindi è adatto alla tua pipeline


2
awk è in grado di essere molto più simile a Perl di quanto pensassi!
Martin DeMello
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.