Je veux une commande Unix pour trouver les lignes entre la première et la dernière occurrence d’un mot
Par exemple:
imaginons que nous avons 1000 lignes. La dixième ligne contient le mot “stackoverflow”, la trente-cinquième ligne contient également le mot “stackoverflow”.
Je veux imprimer des lignes entre 10 et 35 et les écrire dans un nouveau fichier.
Vous pouvez le faire en deux étapes. L’idée de base est de:
1) obtenir le numéro de ligne du premier et du dernier match.
2) imprimer la plage de lignes entre ces plages.
$ read first last <<< $(grep -n stackoverflow your_file | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}') $ awk -vf=$first -vl=$last 'NR>=f && NR<=l' your_file
read first last
lit deux valeurs et les stocke dans $first
et $last
. grep -n stackoverflow your_file
greps et affiche la sortie comme ceci: number_of_line:output
awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
affiche le numéro de la première et dernière correspondance de stackoverflow
dans le fichier. Et
awk -vf=$first -vl=$last 'NR>=f && NR<=l' your_file
imprime toutes les lignes de $first
numéro de ligne jusqu'à $last
numéro de ligne. $ cat a here we have some text stackoverflow and other things bla bla bla bla stackoverflow and whatever else stackoverflow to make more fun blablabla $ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}') $ awk -vf=$first -vl=$last 'NR>=f && NR<=l' a stackoverflow and other things bla bla bla bla stackoverflow and whatever else stackoverflow
Par étapes:
$ grep -n stackoverflow a 3:stackoverflow 9:stackoverflow 11:stackoverflow $ grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}' 3 11 $ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}') $ echo "first=$first, last=$last" first=3, last=11
Si vous connaissez une limite supérieure du nombre de lignes pouvant exister (disons un million), vous pouvez utiliser ce simple script abusif:
(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow) < file
Vous pouvez append | tail -n +2 | head -n -1
| tail -n +2 | head -n -1
| tail -n +2 | head -n -1
pour supprimer également les lignes de bordure:
(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow | tail -n +2 | head -n -1) < file
Je ne suis pas sûr à 100% de la question de savoir si la sortie doit inclure les première et dernière lignes correspondantes, donc je suppose que oui. Mais cela peut être facilement changé si nous voulons à la place exclusive.
Cette solution pure-bash le fait en une seule étape – c.-à-d. Que le fichier (ou le canal) n’est lu qu’une seule fois:
#!/bin/bash function midgrep { while read ln; do [ "$saveline" ] && linea[$((i++))]=$ln if [[ $ln =~ $1 ]]; then if [ "$saveline" ]; then for ((j=0; j
Enregistrez ceci en tant que script (par exemple, midgrep.sh) et dirigez comme suit la sortie de votre choix:
$ cat input.txt | ./midgrep.sh stackoverflow
Cela fonctionne comme suit:
L'avantage de cette approche est que nous ne lisons que les entrées une seule fois. L'inconvénient est que nous mettons tout en mémoire tampon entre chaque match - s'il y a beaucoup de lignes entre chaque match, celles-ci sont toutes mises en mémoire tampon jusqu'à ce que nous trouvions la prochaine correspondance.
En outre, cela utilise l'opérateur d'expression rationnelle bash =~
pour conserver cette pure bash. Mais vous pouvez remplacer cela par un grep si vous êtes plus à l'aise avec cela.
En utilisant perl :
perl -00 -lne ' chomp(my @arr = split /stackoverflow/); print join "\nstackoverflow", @arr[1 .. $#arr -1 ] ' file.txt | tee newfile.txt
L’idée sous-jacente est d’alimenter un tableau de tout le fichier d’entrée en morceaux en utilisant la chaîne “stackoverflow” à séparer. Ensuite, nous imprimons les deuxièmes occurrences à la dernière -1 avec join “stackoverflow”.