Decir que tengo un archivo de texto grande (> 2 GB) y sólo quiero que cat
las líneas X
a Y
(por ejemplo, 57.89 millón a 57,890,010).
Por lo que entiendo, puedo hacer esto head
entrando tail
o viceversa, es decir
head -A /path/to/file | tail -B
o alternativamente
tail -C /path/to/file | head -D
donde A
, B
, C
y D
puede ser calculado a partir del número de líneas en el archivo, X
y Y
.
Pero hay dos problemas con este enfoque:
- Usted tiene que calcular
A
,B
,C
yD
. - Los comandos podrían
pipe
entre sí muchas más líneas de las que estoy interesado en leer (por ejemplo, si estoy leyendo solo unas pocas líneas en medio de un archivo enorme)
¿Hay alguna manera de que el shell simplemente funcione y genere las líneas que quiero? (mientras proporciona solo X
y Y
)?
tail
cat
large-files
head
Amelio Vazquez-Reina
fuente
fuente
Respuestas:
Sugiero la
sed
solución, pero en aras de la integridad,Para cortar después de la última línea:
Prueba de velocidad:
seq 100000000 > test.in
real
tiempo según lo informado porbash
's incorporadotime
Estos no son puntos de referencia precisos, pero la diferencia es clara y lo suficientemente repetible * como para dar una buena idea de la velocidad relativa de cada uno de estos comandos.
*: Excepto entre los dos primeros,
sed -n p;q
yhead|tail
, que parecen ser esencialmente lo mismo.fuente
tail -n +50000000 test.in | head -n10
, que a diferenciatail -n-50000000 test.in | head -n10
daría el resultado correcto?tail+|head
es más rápido en un 10-15% que sed, he agregado ese punto de referencia.-c
para omitir caracteres,tail+|head
es instantánea. Por supuesto, no puede decir "50000000" y puede que tenga que buscar manualmente el inicio de la sección que está buscando.Si desea líneas X a Y inclusive (comenzando la numeración en 1), use
tail
leerá y descartará las primeras líneas X-1 (no hay forma de evitarlo), luego leerá e imprimirá las siguientes líneas.head
leerá e imprimirá el número de líneas solicitado, luego saldrá. Cuandohead
sale,tail
recibe una señal SIGPIPE y muere, por lo que no habrá leído más del tamaño de un búfer (generalmente unos pocos kilobytes) de líneas del archivo de entrada.Alternativamente, como sugirió gorkypl , use sed:
Sin embargo, la solución sed es significativamente más lenta (al menos para las utilidades GNU y Busybox; sed podría ser más competitiva si extrae una gran parte del archivo en un sistema operativo donde la tubería es lenta y sed es rápida). Aquí hay puntos de referencia rápidos en Linux; los datos fueron generados por
seq 100000000 >/tmp/a
, el entorno es Linux / amd64,/tmp
es tmpfs y la máquina está inactiva y no se intercambia.Si conoce el rango de bytes con el que desea trabajar, puede extraerlo más rápido saltando directamente a la posición inicial. Pero para las líneas, debe leer desde el principio y contar las nuevas líneas. Para extraer bloques de x inclusive a y exclusivo a partir de 0, con un tamaño de bloque de b:
fuente
tail will read and discard the first X-1 line
parece evitarse cuando se da el número de líneas desde el final. En tal caso, la cola parece leer hacia atrás desde el final de acuerdo con los tiempos de ejecución. Por favor lea:http://unix.stackexchange.com/a/216614/79743
.tail
(incluida GNU tail) tienen heurísticas para leer desde el final. Eso mejora latail | head
solución en comparación con otros métodos.El
head | tail
enfoque es una de las mejores y más "idiomáticas" formas de hacer esto:Como señaló Gilles en los comentarios, una forma más rápida es
La razón por la que esto es más rápido es que las primeras líneas X - 1 no necesitan pasar por la tubería en comparación con el
head | tail
enfoque.Su pregunta como formulada es un poco engañosa y probablemente explica algunas de sus dudas infundadas hacia este enfoque.
Usted dice que usted tiene que calcular
A
,B
,C
,D
pero como se puede ver, no es necesario el número de líneas del archivo y como máximo 1 cálculo es necesario, que la cáscara puede hacer por usted de todos modos.Le preocupa que la tubería lea más líneas de las necesarias. De hecho, esto no es cierto:
tail | head
es lo más eficiente posible en términos de E / S de archivo. Primero, considere la cantidad mínima de trabajo necesario: para encontrar la línea X 'th en un archivo, la única forma general de hacerlo es leer cada byte y detenerse cuando cuenta X símbolos de nueva línea ya que no hay forma de adivinar el archivo desplazamiento de la línea X 'th. Una vez que llegue a la línea * X *, debe leer todas las líneas para imprimirlas, deteniéndose en la línea Y '. Por lo tanto, ningún enfoque puede salirse con la lectura de menos de líneas Y Ahora,head -n $Y
lee no más que Ylíneas (redondeadas a la unidad de memoria intermedia más cercana, pero las memorias intermedias si se usan correctamente mejoran el rendimiento, por lo que no hay que preocuparse por esa sobrecarga). Además,tail
no leerá más quehead
, por lo tanto, hemos demostrado quehead | tail
lee la menor cantidad posible de líneas (de nuevo, además de un almacenamiento intermedio insignificante que estamos ignorando). La única ventaja de eficiencia de un enfoque de herramienta única que no utiliza tuberías es menos procesos (y, por lo tanto, menos gastos generales).fuente
La forma más ortodoxa (pero no la más rápida, como señaló Gilles anteriormente) sería utilizarla
sed
.En tu caso:
La
-n
opción implica que solo las líneas relevantes se imprimen en stdout.La p al final del número de la línea de llegada significa imprimir líneas en un rango dado. La q en la segunda parte del script ahorra algo de tiempo al omitir el resto del archivo.
fuente
sed
ytail | head
que se acerca a la par, pero resulta quetail | head
es significativamente más rápido (véase mi respuesta ).tail
/head
se consideran más "ortodoxa", ya que el recorte de los extremos de un archivo es precisamente lo que están hechos para. En esos materiales,sed
solo parece ingresar a la imagen cuando se requieren sustituciones, y se expulsa rápidamente de la imagen cuando comienza a suceder algo mucho más complejo, ya que su sintaxis para tareas complejas es mucho peor que AWK, que luego se hace cargo .Si conocemos el rango para seleccionar, desde la primera línea:
lStart
hasta la última línea:lEnd
podríamos calcular:Si conocemos la cantidad total de líneas:
lAll
también podríamos calcular la distancia hasta el final del archivo:Entonces sabremos ambos:
Elegir el más pequeño de todos:
tailnumber
como este:Nos permite usar el comando de ejecución consistentemente más rápido:
Tenga en cuenta el signo más ("+") adicional cuando
$linestart
se selecciona.La única advertencia es que necesitamos el recuento total de líneas, y eso puede tomar un tiempo adicional para encontrarlo.
Como es habitual con:
Algunas veces medidas son:
Tenga en cuenta que los tiempos cambian drásticamente si las líneas seleccionadas están cerca del inicio o cerca del final. Un comando que parece funcionar bien en un lado del archivo, puede ser extremadamente lento en el otro lado del archivo.
fuente
Hago esto con bastante frecuencia y escribí este guión. No necesito encontrar los números de línea, el script lo hace todo.
fuente
tail|head
, que se ha discutido ampliamente en la pregunta y las otras respuestas, y el 90% determina los números de línea donde aparecen las cadenas / patrones especificados, lo que no era parte de la pregunta . PD: siempre debe citar los parámetros y variables de su shell; por ejemplo, "$ 3" y "$ 4".