sed ou awk: supprime n lignes suivant un motif

106

Comment mélanger des modèles et des plages numériques dans sed (ou tout autre outil similaire - awk par exemple)? Ce que je veux faire, c'est faire correspondre certaines lignes dans un fichier et supprimer les n lignes suivantes avant de continuer, et je veux faire cela dans le cadre d'un pipeline.

Martin DeMello
la source

Réponses:

189

Je vais essayer ça.

Pour supprimer 5 lignes après un motif (y compris la ligne avec le motif):

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

Pour supprimer 5 lignes après un motif (à l'exclusion de la ligne avec le motif):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt
dogbane
la source
14
Notez que le +Nmodèle est une extension GNU. Remplacez le premier npar un Ndans votre deuxième exemple pour qu'il inclue la ligne avec le motif.
Suspendu jusqu'à nouvel ordre.
2
comment supprimer toutes les lignes après la correspondance du motif? J'utilise sed -e '/ <! - # content end -> </div> /, $ d' out.txt mais cela donne une erreur en disant: sed: -e expression # 1, char 24: caractères supplémentaires après commande Merci d'avance.
N mol
8
Ce qui se passe est similaire mais légèrement différent dans chaque cas. Dans la première recette, /pattern/,+5définit une plage, qui commence par une ligne contenant "pattern" ( /pattern/) et se termine 5 lignes plus tard ( +5). Le dernier caractère dest une commande à exécuter sur chaque ligne de cette plage, qui est «supprimer». Dans la deuxième recette, au lieu de faire correspondre une plage, il correspond juste à la ligne contenant le motif ( /pattern/), puis exécute une série de commandes {n;N;N;N;N;d}:, qui imprime essentiellement la ligne suivante ( n), puis lit et finalement supprime les 4 lignes suivantes ( N;N;N;N;d).
pimlottc
18
Sur les systèmes Mac / OS X, vous devez ajouter un point-virgule avant le crochet fermant:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL
1
Par souci d'exhaustivité: Pour supprimer toutes les lignes suivant un certain modèle, something faites :,sed -E '/^something$/,$d'-Eest l'expression régulière étendue de portabilité POSIX.
not2qubit
7

Sans extensions GNU (par exemple sur macOS):

Pour supprimer 5 lignes après un motif (y compris la ligne avec le motif)

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

Ajouter -i ''pour modifier sur place.

thakis
la source
6

Des awksolutions simples :

Supposons que l'expression régulière à utiliser pour rechercher les lignes correspondantes est stockée dans la variable shell $regexet le nombre de lignes à ignorer $count.

Si la ligne correspondante doit également être ignorée (les $count + 1lignes sont ignorées):

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

Si la ligne correspondante ne doit pas être ignorée (les $countlignes après le match sont sautées):

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

Explication:

  • -v regex="$regex" -v count="$count"définit des awkvariables basées sur des variables shell du même nom.
  • $0 ~ regex correspond à la ligne d'intérêt
    • { skip=count; next }initialise le compte de sauts et passe à la ligne suivante, sautant effectivement la ligne correspondante; dans la 2ème solution, l' printavant nextgarantit qu'il n'est pas ignoré.
    • --skip >= 0 décrémente le nombre de sauts et prend des mesures s'il est (toujours)> = 0, ce qui implique que la ligne à portée de main doit être ignorée.
    • { next } passe à la ligne suivante, sautant effectivement la ligne actuelle
  • 1est un raccourci couramment utilisé pour { print }; c'est-à-dire que la ligne courante est simplement imprimée
    • Seules les lignes non concordantes et non sautées atteignent cette commande.
    • La raison qui 1équivaut à { print }est qu'il 1est interprété comme un modèle booléen qui, par définition, évalue toujours vrai, ce qui signifie que son action associée (bloc) est exécutée sans condition. Puisqu'il n'y a pas d' action associée dans ce cas, par awkdéfaut , l' impression de la ligne.
mklement0
la source
3

Cela pourrait fonctionner pour vous:

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
potong
la source
10
Wow, c'est cryptique.
pimlottc
3
Une solution intelligente (bien que spécifique à GNU-Sed), mais peu de gens en bénéficieront, sauf si vous ajoutez une explication. pattern_number.txtest un fichier à 2 colonnes contenant le motif à faire correspondre dans la 1ère colonne, et dans la 2ème le nombre de lignes à sauter. La première sedcommande transforme le fichier en un sedscript qui effectue la correspondance et le saut correspondants; ce script est fourni via -fet stdin ( -) à la 2ème sedcommande. La 2ème sedcommande opère sur un exemple de fichier d'entrée ad-hoc formé à partir de la sortie de seq 21pour démontrer que cela fonctionne.
mklement0
En outre, la solution comporte une mise en garde: la méthode qu'elle utilise pour ne pas sauter la première ligne (celle correspondant au modèle) a pour effet secondaire de ne pas sauter les lignes en double dans la plage.
mklement0
C'est une utilisation impressionnante de sed.
Travis Rodman
3

Utiliser 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
$
pile0114106
la source
2

Cette solution vous permet de passer "n" comme paramètre et elle lira vos motifs à partir d'un fichier:

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 -

Le fichier nommé "-" signifie stdin pour awk, il convient donc à votre pipeline

Glenn Jackman
la source
2
awk est capable de ressembler beaucoup plus à Perl que je ne le pensais!
Martin DeMello