Quelle est la bonne façon d'extraire, par exemple, les lignes 20 à 45 d'un énorme fichier texte. Non interactif bien sûr!
Quelle est la bonne façon d'extraire, par exemple, les lignes 20 à 45 d'un énorme fichier texte. Non interactif bien sûr!
Réponses:
tu pourrais essayer:
cat textfile | head -n 45 | tail -n 26
ou
cat textfile | awk "20 <= NR && NR <= 45"
mise à jour:
Comme l'a souligné Mahomedalid, ce cat
n'est pas nécessaire et un peu redondant, mais cela permet une commande claire et lisible.
Si cat
cela vous dérange, une meilleure solution serait:
<textfile awk "20 <= NR && NR <= 45"
,
opérateur de plage awk .
Encore plus simple:
sed -n '20,45p;45q' < textfile
L'indicateur -n désactive la sortie par défaut. Le "20,45" concerne les lignes 20 à 45 inclusivement. La commande "p" imprime la ligne courante. Et le q se ferme après l'impression de la ligne.
q
commande (tout à partir de ;
) a amélioré les performances pour moi lors de l'extraction d'une seule ligne 26995107 d'un fichier de 27169334 lignes.
Ce n'est pas une réponse mais je ne peux pas la poster comme commentaire.
Une autre façon (très rapide) de le faire a été suggérée par mikeserv ici :
{ head -n 19 >/dev/null; head -n 26; } <infile
En utilisant le même fichier de test qu'ici et la même procédure, voici quelques benchmarks (extraction des lignes 1000020-1000045):
mikeserv :
{ head -n 1000019 >/dev/null; head -n 26; } <iplist
real 0m0.059s
Stefan :
head iplist -n 1000045 | tail -n 26
real 0m0.054s
Ce sont de loin les solutions les plus rapides et les différences sont négligeables (pour un seul passage) (j'ai essayé avec différentes gammes: quelques lignes, des millions de lignes etc).
Le faire sans le tuyau pourrait cependant offrir un avantage significatif à une application qui devait rechercher sur plusieurs gammes de lignes de manière similaire, comme:
for pass in 0 1 2 3 4 5 6 7 8 9
do printf "pass#$pass:\t"
head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES
... qui imprime ...
pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000
... et ne lit le fichier qu'une seule fois.
Les autres solutions sed
/ awk
/ perl
lisent tout le fichier et comme il s'agit de fichiers énormes, elles ne sont pas très efficaces. J'ai ajouté quelques alternatives qui exit
ou q
après la dernière ligne de la plage spécifiée:
Stefan :
awk "1000020 <= NR && NR <= 1000045" iplist
real 0m2.448s
contre.
awk "NR >= 1000020;NR==1000045{exit}" iplist
real 0m0.243s
dkagedal ( sed
):
sed -n 1000020,1000045p iplist
real 0m0.947s
contre.
sed '1,1000019d;1000045q' iplist
real 0m0.143s
Steven D :
perl -ne 'print if 1000020..1000045' iplist
real 0m2.041s
contre.
perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist
real 0m0.369s
awk NR==1000020,NR==1000045 textfile
dans votre système.
ruby -ne 'print if 20 .. 45' file
python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'
aussi? :-P C'est quelque chose que Ruby, inspiré de Perl, inspiré de awk / sed, peut facilement faire.
Puisque sed et awk ont déjà été pris, voici une solution perl:
perl -nle "print if ($. > 19 && $. < 46)" < textfile
Ou, comme indiqué dans les commentaires:
perl -ne 'print if 20..45' textfile
perl -ne'print if 20..45' textfile
awk NR==20,NR==45 textfile
fonctionne aussi et lit facilement.