198 votes

Comment obtenir la partie d'un fichier après la première ligne qui correspond à une expression régulière ?

J'ai un fichier d'environ 1000 lignes. Je veux la partie de mon fichier qui se trouve après la ligne qui correspond à mon instruction grep.

C'est-à-dire :

cat file | grep 'TERMINATE'     # It is found on line 534

Je veux donc le fichier de la ligne 535 à la ligne 1000 pour un traitement ultérieur.

Comment puis-je faire cela ?

37 votes

UUOC (Useless Use of cat) : grep 'TERMINATE' file

44 votes

Je le sais, c'est comme ça que je l'utilise. Revenons à la question.

4 votes

Il s'agit d'une question de programmation tout à fait pertinente, qui convient parfaitement à stackoverflow.

353voto

jfgagne Points 4307

Le texte suivant imprime la ligne correspondante TERMINATE jusqu'à la fin du fichier :

sed -n -e '/TERMINATE/,$p'

Expliqué : -n désactive le comportement par défaut de sed d'imprimer chaque ligne après avoir exécuté son script sur celle-ci, -e a indiqué un script pour sed , /TERMINATE/,$ est une sélection de plage d'adresses (lignes), ce qui signifie que la première ligne correspondant à l'adresse TERMINATE (comme grep) à la fin du fichier ( $ ), et p est la commande print qui imprime la ligne en cours.

L'impression se fera à partir de la ligne qui suit la ligne correspondante. TERMINATE jusqu'à la fin du fichier : (depuis APRÈS la ligne correspondante jusqu'à EOF, sans inclure la ligne correspondante)

sed -e '1,/TERMINATE/d'

Expliqué : 1,/TERMINATE/ est une sélection de plage d'adresses (lignes), c'est-à-dire de la première ligne pour l'entrée à la première ligne correspondant à l'adresse TERMINATE expression régulière, et d est la commande delete qui supprime la ligne en cours et passe à la ligne suivante. En tant que sed Le comportement par défaut est d'imprimer les lignes, il imprimera les lignes après TERMINATE jusqu'à la fin de l'entrée.

Si vous souhaitez que les lignes précédant TERMINATE :

sed -e '/TERMINATE/,$d'

Et si vous voulez les deux lignes avant et après TERMINATE dans deux fichiers différents en un seul passage :

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

Les fichiers avant et après contiendront la ligne avec terminate, donc pour traiter chacun d'entre eux vous devez utiliser :

head -n -1 before
tail -n +2 after

SI vous ne voulez pas coder en dur les noms de fichiers dans le sed script, vous pouvez le faire :

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

Mais il faut ensuite s'échapper de la $ ce qui signifie la dernière ligne pour que l'interpréteur de commandes n'essaie pas d'étendre le fichier $w (notez que nous utilisons maintenant des guillemets doubles autour du script au lieu de guillemets simples).

J'ai oublié de préciser que la nouvelle ligne est importante après les noms de fichiers dans le script pour que sed sache que les noms de fichiers se terminent.

Comment remplacer le code TERMINATE par une variable ?

Vous devez créer une variable pour le texte correspondant et procéder de la même manière que dans l'exemple précédent :

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

pour utiliser une variable pour le texte correspondant dans les exemples précédents :

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"

## Print from the line that follows the line containing the
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"

## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

Les points importants concernant le remplacement du texte par des variables dans ces cas sont les suivants :

  1. Variables ( $variablename ) inclus dans single quotes [ ' ne se développera pas, mais les variables à l'intérieur de double quotes [ " Le [ ] sera. Vous devez donc modifier tous les single quotes a double quotes s'ils contiennent du texte que vous souhaitez remplacer par une variable.
  2. En sed contiennent également un $ et sont immédiatement suivis d'une lettre comme : $p , $d , $w . Ils ressembleront également à des variables à développer, vous devez donc les échapper $ avec une barre oblique inverse [ \ ] comme : \$p , \$d , \$w .

1 votes

Comment récupérer les lignes avant TERMINATE et supprimer tout ce qui suit ?

0 votes

Comment remplacer le TERMINAL codé en dur par une variable ?

2 votes

Un cas d'utilisation qui manque ici est la façon d'imprimer les lignes après le dernier marqueur (s'il peut y en avoir plusieurs dans le fichier comme les fichiers journaux, etc).

77voto

aioobe Points 158466

En guise d'approximation simple, vous pouvez utiliser

grep -A100000 TERMINATE file

qui recherche TERMINATE et produit jusqu'à 100 000 lignes suivant cette ligne.

De la page de manuel :

-A NUM, --after-context=NUM

Imprimer NUM lignes de contexte après les lignes correspondantes. Place une ligne contenant un séparateur de groupe (--) entre groupes contigus de correspondances. Avec l'option -o ou --only-matching cela n'a aucun effet et un avertissement est donné.

1 votes

Cela pourrait fonctionner, mais je dois le coder dans mon script pour traiter de nombreux fichiers. Donc, montrez une solution générique.

4 votes

Je pense que c'est une solution pratique !

2 votes

Similairement -B NUM, --before-context=NUM Imprime NUM lignes de contexte avant les lignes correspondantes. Place une ligne contenant un séparateur de groupe (--) entre les groupes contigus de correspondances. Avec l'option -o ou --only-matching, ceci n'a aucun effet et un avertissement est donné.

28voto

Jos De Graeve Points 41

Un outil à utiliser ici est AWK :

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

Comment cela fonctionne-t-il ?

  1. Nous fixons la variable "found" à zéro, en évaluant faux
  2. si l'expression régulière trouve une correspondance pour 'TERMINATE', nous lui attribuons la valeur 1.
  3. Si notre variable 'found' est évaluée à True, on imprime :)

Les autres solutions peuvent consommer beaucoup de mémoire si vous les utilisez pour des fichiers très volumineux.

0 votes

Simple, élégant et très générique. Dans mon cas, il s'agissait d'imprimer tout jusqu'à la deuxième occurrence de '###' : cat file | awk 'BEGIN{ found=0} /###/{found=found+1} {if (found<2) print }'

6 votes

Un outil pas à utiliser ici est cat . awk est parfaitement capable de prendre un ou plusieurs noms de fichiers comme arguments. Voir aussi stackoverflow.com/questions/11710552/useless-use-of-cat

5voto

Mu Qiao Points 3199

Utilisez l'expansion des paramètres Bash comme suit :

content=$(cat file)
echo "${content#*TERMINATE}"

0 votes

Pouvez-vous expliquer ce que vous faites ?

0 votes

J'ai copié le contenu de "file" dans la variable $content. J'ai ensuite supprimé tous les caractères jusqu'à ce que je voie "TERMINATE". Je n'ai pas utilisé de correspondance avide, mais vous pouvez utiliser la correspondance avide par ${content##*TERMINATE}.

0 votes

Voici le lien du manuel de bash : gnu.org/software/bash/manual/

3voto

jfgagne Points 4307

Si, pour quelque raison que ce soit, vous souhaitez éviter d'utiliser sédimentaire Le texte suivant imprimera la ligne correspondante TERMINATE jusqu'à la fin du fichier :

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

Et ce qui suit s'imprimera à partir de la ligne suivante correspondant à TERMINATE jusqu'à la fin du fichier :

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

Il faut deux processus pour faire ce que sed peut faire en un seul processus, et si le fichier change entre l'exécution de grep et de tail, le résultat peut être incohérent, c'est pourquoi je recommande d'utiliser sed. De plus, si le fichier ne contient pas de TERMINATE la première commande échoue.

0 votes

Le fichier est scanné deux fois. que se passe-t-il si le fichier fait 100GB ?

1 votes

Downvoted parce que c'est une solution merdique, mais upvoted parce que 90% de la réponse est constituée de caveats.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X