Dire que j'ai un énorme fichier texte (> 2 Go) et je veux juste cat
les lignes X
à Y
(par exemple , de 57.890.000 à 57.890.010).
D'après ce que je comprends que je peux le faire par la tuyauterie head
dans tail
ou vice - versa, à savoir
head -A /path/to/file | tail -B
Ou bien
tail -C /path/to/file | head -D
où A
, B
, C
et D
peut être calculé à partir du nombre de lignes dans le fichier, X
et Y
.
Mais il y a deux problèmes avec cette approche:
- Vous devez calculer
A
,B
,C
etD
. - Les commandes peuvent
pipe
transmettre beaucoup plus de lignes que ce qui m’intéresse (par exemple, si je ne lis que quelques lignes au milieu d’un fichier volumineux)
Existe-t-il un moyen de faire en sorte que le shell travaille et génère les lignes que je veux? (en ne fournissant que X
et Y
)?
tail
cat
large-files
head
Amelio Vazquez-Reina
la source
la source
Réponses:
Je suggère la
sed
solution, mais dans un souci de complétude,Pour couper après la dernière ligne:
Test de rapidité:
seq 100000000 > test.in
real
temps tel que rapporté parbash
« s builtintime
Ce ne sont en aucun cas des points de repère précis, mais la différence est claire et suffisamment répétable * pour donner une bonne idée de la vitesse relative de chacune de ces commandes.
*: Sauf entre les deux premiers
sed -n p;q
ethead|tail
qui semblent être essentiellement les mêmes.la source
tail -n +50000000 test.in | head -n10
qui, contrairementtail -n-50000000 test.in | head -n10
, donnerait le résultat correct?tail+|head
est plus rapide de 10-15% que sed, j'ai ajouté cette référence.-c
pour sauter des caractères,tail+|head
c'est instantané. Bien sûr, vous ne pouvez pas dire "50000000" et vous devrez peut-être rechercher manuellement le début de la section que vous recherchez.Si vous voulez les lignes X à Y inclus (en commençant par la numérotation à 1), utilisez
tail
lit et supprime les premières lignes X-1 (il n’ya aucun moyen de contourner cela), puis lit et affiche les lignes suivantes.head
lira et imprimera le nombre de lignes demandé, puis quittera. Lorsqu’ilhead
quitte,tail
reçoit un signal SIGPIPE et meurt. Ainsi, il n’aura pas lu plus de la taille d’une mémoire tampon (généralement quelques kilo-octets) de lignes à partir du fichier d’entrée.Sinon, comme suggéré par gorkypl , utilisez sed:
La solution sed est toutefois beaucoup plus lente (du moins pour les utilitaires GNU et Busybox; sed pourrait être plus compétitif si vous extrayez une grande partie du fichier sur un système d'exploitation où la tuyauterie est lente et sed est rapide). Voici des repères rapides sous Linux; les données ont été générées par
seq 100000000 >/tmp/a
, l'environnement est Linux / amd64,/tmp
tmpfs et la machine est sinon inactive et non permutée.Si vous connaissez la plage d'octets avec laquelle vous souhaitez travailler, vous pouvez l'extraire plus rapidement en passant directement à la position de départ. Mais pour les lignes, vous devez lire depuis le début et compter les nouvelles lignes. Pour extraire des blocs de x inclus à y exclusif à partir de 0, avec une taille de bloc de b:
la source
tail will read and discard the first X-1 line
semble être évité lorsque le nombre de lignes est donné à partir de la fin. Dans ce cas, tail semble lire à partir de la fin en fonction des temps d'exécution. S'il vous plaît lire:http://unix.stackexchange.com/a/216614/79743
.tail
(y compris GNU tail) ont une heuristique à lire à partir de la fin. Cela améliore latail | head
solution par rapport aux autres méthodes.L’
head | tail
approche est l’un des meilleurs moyens, parmi les plus "idiomatiques", de le faire:Comme Gilles le souligne dans les commentaires, un moyen plus rapide est de
La raison pour laquelle cela est plus rapide est que les premières lignes X-1 n'ont pas besoin de passer par le tuyau par rapport à l'
head | tail
approche.Votre question, dans sa formulation, est un peu trompeuse et explique probablement certaines de vos craintes non fondées à l’égard de cette approche.
Vous dites que vous devez calculer
A
,B
,C
,D
mais comme vous pouvez le voir, le nombre de lignes du fichier n'est pas nécessaire et il est nécessaire de calcul au plus 1, la coque peut faire pour vous de toute façon.Vous craignez que la tuyauterie lise plus de lignes que nécessaire. En fait, ce n’est pas vrai:
tail | head
c’est aussi efficace que possible en termes d’E / S sur fichier. Considérons d' abord le montant minimum de travail nécessaire: pour trouver le X e ligne » dans un fichier, la seule manière générale de le faire est de lire chaque octet et arrêter quand vous comptez X symboles de nouvelle ligne car il n'y a aucun moyen de deviner le fichier décalage de la X 'ème ligne. Une fois que vous avez atteint la * X * ème ligne, vous devez lire toutes les lignes afin de les imprimer, en vous arrêtant à la Y 'e ligne. Ainsi, aucune approche ne peut se permettre de lire moins de lignes Y. Maintenant,head -n $Y
ne lit pas plus que Ylignes (arrondies à l’unité tampon la plus proche, mais les tampons, s’ils sont utilisés correctement, améliorent les performances, évitant ainsi de s’inquiéter de cette surcharge). En outre,tail
ne lisons pas plus quehead
, nous avons donc montré que noushead | tail
lisions le moins de lignes possible (encore une fois, plus une mise en mémoire tampon négligeable que nous ignorons). Le seul avantage en termes d'efficacité d'une approche à un seul outil qui n'utilise pas de canalisations est la réduction du nombre de processus (et donc de la surcharge).la source
Le moyen le plus orthodoxe (mais pas le plus rapide, comme l'a noté Gilles ci-dessus) serait d'utiliser
sed
.Dans ton cas:
L'
-n
option implique que seules les lignes pertinentes sont imprimées sur stdout.Le p à la fin du numéro de ligne d'arrivée signifie que vous imprimez des lignes dans une plage donnée. Le q dans la deuxième partie du script permet de gagner du temps en ignorant le reste du fichier.
la source
sed
ettail | head
d'être à égalité, mais il se trouve quetail | head
est nettement plus rapide (voir ma réponse ).tail
/head
sont considérés comme plus « orthodoxes », depuis la coupe ou l' autre extrémité d'un fichier est précisément ce qu'ils sont faits pour. Dans ces documents, ilsed
ne semble y avoir d’inconvénient que lorsque des substitutions sont nécessaires - et d’être rapidement exclu dès que quelque chose de beaucoup plus complexe commence à se produire, car sa syntaxe pour les tâches complexes est bien pire que AWK, qui prend ensuite le relais. .Si nous connaissons la plage à sélectionner, de la première ligne:
lStart
à la dernière ligne:lEnd
nous pourrions calculer:Si nous connaissons le nombre total de lignes:
lAll
nous pourrions aussi calculer la distance jusqu'à la fin du fichier:Ensuite, nous connaîtrons les deux:
Choisir le plus petit de ceux-ci:
tailnumber
comme ceci:Nous permet d'utiliser la commande d'exécution la plus rapide de tous les temps:
Veuillez noter le signe plus ("+") supplémentaire lorsque
$linestart
est sélectionné.Le seul inconvénient est que nous avons besoin du nombre total de lignes, ce qui peut prendre un peu plus de temps à trouver.
Comme d'habitude avec:
Quelques temps mesurés sont:
Notez que les temps changent radicalement si les lignes sélectionnées sont proches du début ou de la fin. Une commande qui semble bien fonctionner d'un côté du fichier peut être extrêmement lente de l'autre côté du fichier.
la source
Je le fais assez souvent et j'ai donc écrit ce script. Je n'ai pas besoin de trouver les numéros de ligne, le script fait tout.
la source
tail|head
, ce qui a été longuement discuté dans la question et les autres réponses, et 90% déterminant les numéros de ligne où des chaînes / motifs spécifiés apparaissent, ce qui ne faisait pas partie de la question . PS vous devriez toujours citer vos paramètres de shell et vos variables; par exemple, "$ 3" et "$ 4".