Esto es para la tarea, pero no haré la pregunta específica de la tarea.
Necesito usar head y tail para tomar diferentes conjuntos de líneas de un archivo. Entonces, como las líneas 6-11 y las líneas 19-24, guárdelas en otro archivo. Sé que puedo hacer esto usando un anexo como
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
Pero no creo que debamos hacerlo.
¿Existe alguna forma específica de combinar los comandos head y tail y luego guardarlos en el archivo?
Respuesta1
Puedes hacerlo head
solo con aritmética básica, si agrupas comandos { ... ; }
usando una construcción como
{ head -n ...; head -n ...; ...; } < input_file > output_file
donde todos los comandos comparten la misma entrada (gracias@mikeserv).
Obtener las líneas 6-11 y las líneas 19-24 equivale a:
head -n 5 >/dev/null # dump the first 5 lines to `/dev/null` then
head -n 6 # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6 # then print the next 6 lines (19 up to 24)
Entonces, básicamente, ejecutarías:
{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file
Respuesta2
Puede utilizar la { … }
construcción de agrupación para aplicar el operador de redirección a un comando compuesto.
{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1
En lugar de duplicar las primeras M+N líneas y conservar solo la última N, puede omitir las primeras M líneas y duplicar las siguientes N. Esto esMediblemente más rápido en archivos grandes.. Tenga en cuenta que el +N
argumento de tail
no es el número de líneas a omitir, sino una más: es el número de la primera línea a imprimir con líneas numeradas desde 1.
{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1
De cualquier manera, el archivo de salida solo se abre una vez, pero el archivo de entrada se recorre una vez para cada fragmento que se extrae. ¿Qué tal agrupar las entradas?
{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1
En general, esto no funciona. (Podría funcionar en algunos sistemas, al menos cuando la entrada es un archivo normal). ¿Por qué? Porquealmacenamiento en búfer de entrada. La mayoría de los programas, incluido tail
, no leen su entrada byte a byte, sino unos pocos kilobytes a la vez, porque es más rápido. Entonces tail
lee unos pocos kilobytes, salta un poco al principio, pasa un poco más a head
y se detiene, pero lo que se lee se lee y no está disponible para el siguiente comando.
Otro enfoquees usar head
tubería para/dev/null
para saltar líneas.
{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1
Nuevamente, no se garantiza que esto funcione debido al almacenamiento en búfer. Funciona con el head
comando de GNU coreutils (el que se encuentra en sistemas Linux no integrados), cuando la entrada proviene de un archivo normal. Esto se debe a que una vez que esta implementación head
ha leído lo que quiere,establece la posición del archivoal primer byte que no generó. Esto no funciona si la entrada es una tubería.
Una forma más sencilla de imprimir varias secuencias de líneas de un archivo es llamar a una herramienta más generalista comosedoawk. (Esto puede ser más lento, pero sólo es importante para archivos extremadamente grandes).
sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1
Respuesta3
Sé que dijiste que necesitas usar head y tail, pero sed es definitivamente la herramienta más sencilla para este trabajo.
$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1
Incluso puedes construir los bloques en una cadena con algún otro proceso y ejecutarlo a través de sed.
$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1
-n niega la salida, luego especifica los rangos para imprimir con p, con el primer y último número del rango separados por una coma.
Dicho esto, puedes hacer la agrupación de comandos que sugirió @don_crissti, o recorrer el archivo varias veces con la cabeza y la cola tomando un trozo de líneas cada vez que lo haces.
$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1
Cuantas más líneas haya en un archivo y más bloques tenga, más eficiente será sed.
Respuesta4
Utilice una función bash como esta:
seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24
Esto es un poco excesivo en este caso, pero si los filtros crecen, puede convertirse en una bendición.