J'ai un fichier avec environ 1000 lignes. Je veux la partie de mon fichier après la ligne qui correspond à mon instruction grep.
c'est à dire.
$ cat file | grep 'TERMINATE' // Its found on line 534
Je souhaite donc que le fichier de la ligne 535 to line 1000
soit traité ultérieurement.
Comment fait-on ça ?
Ce qui suit imprimera la ligne correspondant à TERMINATE
jusqu'à la fin du fichier:
sed -n -e '/TERMINATE/,$p'
Expliqué: -n
désactive le comportement par défaut de sed
lors de l'impression de chaque ligne après l'exécution de son script, -e
a indiqué un script à sed
, /TERMINATE/,$
est une sélection de plage d'adresse (ligne) signifiant la première ligne correspondant à l'expression régulière TERMINATE
(comme grep) à la fin du fichier ($
), et p
est la commande d'impression qui affiche la ligne en cours.
Cela imprimera à partir de la ligne qui suit la ligne correspondant à TERMINATE
jusqu'à la fin du fichier:
(de APRES la ligne correspondante à EOF, n'incluant pas la ligne correspondante)
sed -e '1,/TERMINATE/d'
Expliqué: 1,/TERMINATE/
est une sélection de plage d'adresse (ligne) signifiant la première ligne pour l'entrée de la 1re ligne correspondant à l'expression régulière TERMINATE
, et d
est la commande de suppression qui supprime la ligne actuelle et passe à la ligne suivante. Le comportement par défaut de sed
étant d'imprimer les lignes, les lignes après TERMINATE
seront imprimées jusqu'à la fin de l'entrée.
Modifier:
Si vous voulez les lignes avant TERMINATE
:
sed -e '/TERMINATE/,$d'
Et si vous voulez les deux lignes avant et après TERMINATE
dans 2 fichiers différents en un seul passage:
sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file
Les fichiers before et after contiendront la ligne avec terminate, donc pour traiter chacun d’entre eux, vous devez utiliser:
head -n -1 before
tail -n +2 after
Edit2:
SI vous ne voulez pas coder en dur les noms de fichiers dans le script sed, vous pouvez:
before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file
Mais vous devez ensuite échapper à $
, qui signifie la dernière ligne, pour éviter que le shell n'essaie d'élargir la variable $w
(notez que nous utilisons maintenant des guillemets doubles autour du script plutôt que des guillemets simples).
J'ai oublié de dire que la nouvelle ligne est importante après les noms de fichiers dans le script afin que sed sache que les noms de fichiers se terminent.
Edit: 2016-0530
Sébastien Clément a demandé: "Comment pourriez-vous remplacer la TERMINATE
codée en dur par une variable?"
Vous pouvez créer une variable pour le texte correspondant, puis 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
utiliser une variable pour le texte correspondant avec 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 de texte par des variables dans ces cas sont les suivants:
$variablename
) incluses dans single quotes
['
] ne "se développeront" pas, mais les variables à l'intérieur de double quotes
["
] le seront. Donc, vous devez changer tous les single quotes
en double quotes
s’ils contiennent du texte que vous voulez remplacer par une variable. sed
contiennent également un $
et sont immédiatement suivies d'une lettre du type: $p
, $d
, $w
. Elles ressembleront également à des variables à développer. Vous devez donc échapper ces caractères $
avec une barre oblique inversée [\
] telle que: \$p
, \$d
, \$w
.Comme une approximation simple, vous pouvez utiliser
grep -A100000 TERMINATE file
qui greps pour TERMINATE
et génère jusqu'à 100000 lignes suivant cette ligne.
De la page de manuel
-A NUM, --after-context=NUM
Affiche NUM lignes du contexte de fin après les lignes correspondantes. Place une ligne contenant un séparateur de groupe (-) entre groupes de matches contigus. Avec le -o ou --only-matching- option, cela n'a aucun effet et un avertissement est donné.
Un outil à utiliser ici est awk:
cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1} {if (found) print }'
Comment cela marche-t-il:
Les autres solutions risquent de consommer beaucoup de mémoire si vous les utilisez sur des fichiers très volumineux.
Utilisez le développement des paramètres bash comme suit:
content=$(cat file)
echo "${content#*TERMINATE}"
Si je comprends bien votre question, vous souhaitez que les lignes aprèsTERMINATE
, sans la TERMINATE
- line. awk
peut le faire de manière simple:
awk '{if(found) print} /TERMINATE/{found=1}' your_file
Explication:
if(found) print
) n’imprimera rien pour commencer.Ceci imprimera toutes les lignes après la ligne TERMINATE
-.
Généralisation:
Exemple:
$ cat ex_file.txt
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt
A good line to include
And this line
Yep
$
Explication:
found
est défini.found=1
pour que les lignes suivantes soient imprimées. Notez que cette vérification est effectuée après l'impression réelle pour exclure la ligne start du résultat.Remarques:
BEGIN{found=0}
au début de l'expression awk.Si, pour une raison quelconque, vous souhaitez éviter d'utiliser sed, les opérations suivantes imprimeront la ligne correspondant à TERMINATE
jusqu'à la fin du fichier:
tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file
et ce qui suit sera imprimé à 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 2 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. Je recommande donc d'utiliser sed. De plus, si le fichier ne contient pas TERMINATE
, la 1ère commande échoue.
Il y a plusieurs façons de le faire avec sed
ou awk
:
sed -n '/TERMINATE/,$p' file
Ceci recherche TERMINATE
dans votre fichier et imprime à partir de cette ligne jusqu'à la fin du fichier.
awk '/TERMINATE/,0' file
C'est exactement le même comportement que sed
.
Si vous connaissez le numéro de la ligne à partir de laquelle vous souhaitez imprimer, vous pouvez le spécifier avec NR
(numéro de l'enregistrement, qui indique le numéro de la ligne):
awk 'NR>=535' file
$ seq 10 > a #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10
grep -A 10000000 Fichier 'TERMINATE'
Cela pourrait être une façon de le faire. Si vous savez quelle ligne du fichier vous avez votre grep Word et combien de lignes vous avez dans votre fichier:
grep -A466 Fichier 'TERMINATE'
Alternatives à l'excellente réponse sed
de jfgagne et qui n'incluent pas la ligne correspondante:
awk '/TERMINATE/ {y=1;next} y'
( https://stackoverflow.com/a/18166628 )awk '/TERMINATE/ ? c++ : c'
( https://stackoverflow.com/a/23984891 )Perl -ne 'print unless 1 .. /TERMINATE/'
( https://stackoverflow.com/a/18167194 )