Comment afficher certaines lignes d'un fichier texte sous Linux?


86

Je suppose que tout le monde connaît les utilitaires de ligne de commande Linux utiles headet tail. headvous permet d'imprimer les X premières lignes d'un fichier, tailfait la même chose mais affiche la fin du fichier. Qu'est-ce qu'une bonne commande pour imprimer au milieu d'un fichier? quelque chose comme middle --start 10000000 --count 20(imprimer les lignes 10'000'000 à 10'000'010).

Je cherche quelque chose qui traitera efficacement les gros fichiers. J'ai essayé tail -n 10000000 | head 10et c'est horriblement lent.


Réponses:


112
sed -n '10000000,10000020p' filename

Vous pourriez peut-être accélérer cela un peu comme ceci:

sed -n '10000000,10000020p; 10000021q' filename

Dans ces commandes, l'option -namène sedà « supprimer l' impression automatique de l' espace de motif ». La pcommande "imprime le motif actuel" et la qcommande "Quitte immédiatement le script sed sans traitement supplémentaire ..." Les guillemets proviennent de la sed manpage .

Au fait, votre commande

tail -n 10000000 filename | head 10

commence à la dix millionième ligne à partir de la fin du fichier, tandis que votre commande "moyenne" semble commencer au dix millionième à partir du début, ce qui équivaut à:

head -n 10000010 filename | tail 10

Le problème est que pour les fichiers non triés avec des lignes de longueur variable, tout processus doit passer par le décompte des fichiers. Il n'y a aucun moyen de raccourcir cela.

Toutefois, si le fichier est trié (un fichier journal avec des horodatages, par exemple) ou s'il comporte des lignes de longueur fixe, vous pouvez rechercher dans le fichier en fonction d'une position d'octet. Dans l'exemple de fichier journal, vous pouvez effectuer une recherche binaire plusieurs fois, comme le fait mon script Python ici *. Dans le cas du fichier à longueur d'enregistrement fixe, c'est très simple. Vous recherchez simplement des linelength * linecountcaractères dans le fichier.

* Je continue à vouloir publier une autre mise à jour de ce script. Peut-être que je vais en parler un de ces jours.


Voici une sedversion de Charles middlefonction: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Il gérera plusieurs arguments de fichiers, noms de fichiers avec des espaces, etc. Plusieurs fichiers sont traités ensemble comme s'ils avaient été traités de la même manière que d'habitude sed(de sorte que le milieu 1000 100 fichier1 fichier2 s'étendrait de la fin du premier fichier au début du second si le premier a moins de 1100 lignes).
Dennis Williamson

La fonction de mon commentaire précédent peut être appelée avec un paramètre de nom de fichier: middle startline count filenameou plusieurs noms de fichiers: middle startline count file1 file2 file3ou avec une redirection: middle startline count < filenameou dans un tuyau: nombre some_command | moyen de lignes de départ, oucat file* | middle startline count
Dennis Williamson

Le `dans votre commande sed ne devrait-il pas être un '? Je ne peux pas le faire fonctionner avec le backtick mais cela fonctionne bien avec la citation simple.
Ian Hunter

@beanland: Oui, c'est une faute de frappe. Je l'ai réparé. Merci.
Dennis Williamson

1
@kev: J'ai ajouté quelques explications à ma réponse.
Dennis Williamson

28

J'ai découvert l'utilisation suivante de sed

sed -n '10000000,+20p'  filename

J'espère que c'est utile à quelqu'un!


Il est bon de savoir qu’il existe une alternative à l’argument de la dernière ligne proposé par Dennis: une ligne compte comme deuxième sed -nargument, ce qui le rend très lisible.
user3123159

Un exemple d'utilisation: extract_lines(){sed -n "$1,+$2p" <file>}qui écrit sur stdout.
user3123159

4

C'est la première fois que je publie ici! Quoi qu'il en soit, celui-ci est facile. Supposons que vous souhaitiez extraire la ligne 8872 de votre fichier appelée fichier.txt. Voici comment vous le faites:

cat -n fichier.txt | grep '^ * 8872'

Maintenant, la question est de trouver 20 lignes après cela. Pour ce faire, vous faites

cat -n fichier.txt | grep -A 20 '^ * 8872'

Pour les lignes autour ou avant, voir les drapeaux -B et -C dans le manuel grep.


Bien que ce soit techniquement correct et un moyen intéressant de le faire sur un fichier de taille raisonnable, je suis curieux de savoir son efficacité lorsque vous travaillez avec des fichiers de la taille demandée par l'affiche.
Jenny D

Plusieurs lignes: cat -n fichier.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight

cat -n file.txt | grep '^ *1'donne toutes les lignes qui ont 1 sur leur côté droit. Comment sortir la ligne 1 avec cette technique? Je sais que je peux diriger -n 1 .... mais comment utiliser grep?
Sean87

1

La réponse séduite de Dennis est la voie à suivre. Mais en utilisant juste la tête et la queue, sous bash:

middle () {head -n $ [1 $ + 2 $] | tail -n $ 2; }

Cela balaye deux fois les premières lignes à 1 $ + 2 $, ce qui est bien pire que la réponse de Dennis. Mais vous n'avez pas besoin de vous souvenir de toutes ces lettres sed pour l'utiliser ...


L'utilisation $[...]est déconseillée, du moins en Bash. En outre, il vous manque un paramètre de fichier.
Dennis Williamson

@Dennis: Aucun paramètre manquant: vous êtes censé utiliser cela sur stdin, comme indiqué par middle 10 10 < /var/log/auth.log.
Charles Stewart

1

Utilisez la commande suivante pour obtenir la plage de lignes particulière

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Ici debug.log est mon fichier qui consiste en un manque de lignes et j’imprimais les lignes de 1220974 à 1513793 dans un fichier test.log. espérons que cela vous aidera à capturer la gamme de lignes.


La même réponse que serverfault.com/a/641252/140016 . Moins voté.
Deer Hunter

Ce n'est pas la même réponse. Cela devrait être plus rapide pour les gros fichiers car il abandonne après l'impression de la dernière ligne au lieu de continuer à parcourir le fichier.
phobique

0

Une version ruby ​​oneliner.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Cela peut être utile à quelqu'un. Les solutions avec 'sed' fournies par Dennis et Dox sont très sympas, même parce que cela semble plus rapide.


0

Vous pouvez utiliser 'nl'.

nl filename | grep <line_num>

0

Par exemple, ce awk imprimera des lignes entre 20 et 40

awk '{if ((NR> 20) && (NR <40)) print $ 0}' / etc / passwd


0

Si vous connaissez les numéros de ligne, dites que vous voulez obtenir les lignes 1, 3 et 5 d'un fichier, dites / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.