Usar head y tail para capturar diferentes conjuntos de líneas y guardarlas en el mismo archivo

Usar head y tail para capturar diferentes conjuntos de líneas y guardarlas en el mismo archivo

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 headsolo 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 +Nargumento de tailno 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 taillee unos pocos kilobytes, salta un poco al principio, pasa un poco más a heady se detiene, pero lo que se lee se lee y no está disponible para el siguiente comando.

Otro enfoquees usar headtubería para/dev/nullpara 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 headcomando 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 headha 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.

información relacionada