¿Cómo obtener un comportamiento inverso para `cola` y` cabeza`?

55

¿Hay alguna manera de head/ tailun documento y obtener la salida inversa; porque no sabes cuántas líneas hay en un documento?

Es decir, solo quiero obtener todo menos las 2 primeras líneas foo.txtpara agregar a otro documento.

chrisjlee
fuente

Respuestas:

57

Puede usar esto para quitar las dos primeras líneas:

tail -n +3 foo.txt

y esto para quitar las dos últimas líneas:

head -n -2 foo.txt

(suponiendo que el archivo termine con \nel último)


Al igual que para el uso estándar de taily headestas operaciones no son destructivas. Úselo >out.txtsi desea redirigir la salida a algún archivo nuevo:

tail -n +3 foo.txt >out.txt

En el caso de que out.txtya exista, sobrescribirá este archivo. Use en >>out.txtlugar de >out.txtsi prefiere que se agregue la salida out.txt.

Stéphane Gimenez
fuente
3
re "head, cuando el archivo termina con \n" .. Funciona para todos los enteros negativos distintos de los -n -0cuales no devuelve nada , tal como lo -n 0haría (usando: head (GNU coreutils) 7.4) ... Sin embargo, cuando hay un final \n, -n -0se imprime como podría esperarse de la -, es decir. imprime todo el archivo ... Por lo tanto, funciona para todos los valores negativos distintos de cero ... pero -0falla cuando no hay seguimiento\n
Peter.O
@fred: Extraño de hecho ... (lo mismo con 8.12 aquí).
Stéphane Gimenez
¿Es esta operación destructiva? ¿Como quiero que copie el inverso de las dos primeras líneas del documento a otro?
chrisjlee
@ Chris: No, simplemente imprimen el resultado en su "salida estándar", que generalmente está conectada al terminal. He agregado algunos detalles sobre cómo redirigir la salida a algunos archivos.
Stéphane Gimenez
77
head -n -2no es compatible con POSIX .
l0b0
9

Si desea todas menos las primeras líneas N-1, llame tailcon el número de líneas +N. (El número es el número de la primera línea que desea conservar, comenzando en 1, es decir, +1 significa comenzar en la parte superior, +2 significa omitir una línea y así sucesivamente).

tail -n +3 foo.txt >>other-document

No hay una forma fácil y portátil de omitir las últimas N líneas. GNU headpermite head -n +Ncomo contrapartida de tail -n +N. De lo contrario, si tiene tac(por ejemplo, GNU o Busybox), puede combinarlo con tail:

tac | tail -n +3 | tac

Portablemente, puede usar un filtro awk (no probado):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Si desea eliminar las últimas líneas de un archivo grande, puede determinar el desplazamiento de bytes de la pieza a truncar y luego realizar el truncamiento con dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

No puede truncar un archivo en su lugar al principio, aunque si necesita eliminar las primeras líneas de un archivo enorme, puede mover el contenido .

Gilles 'SO- deja de ser malvado'
fuente
En algunos sistemas (como el Linux moderno), puede truncar (colapsar) un archivo en su lugar al principio, pero generalmente solo en una cantidad que es múltiplo del tamaño del bloque FS (por lo que no es realmente útil en este caso).
Stéphane Chazelas
3

Desde la tailpágina del manual (GNU tail, es decir):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Por lo tanto, lo siguiente debería agregar todas menos las 2 primeras líneas de somefile.txta anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Steven Monday
fuente
3

Para eliminar las primeras n líneas, se puede utilizar GNU sed. Por ejemplo si n = 2

sed -n '1,2!p' input-file

La !media "excluye este intervalo". Como puede imaginar, se pueden obtener resultados más complicados, por ejemplo

sed -n '3,5p;7p'

eso mostrará la línea 3,4,5,7. Más poder proviene del uso de expresiones regulares en lugar de direcciones.

La limitación es que los números de las líneas deben conocerse de antemano.

enzotib
fuente
1
¿Por qué no solo sed 1,2d? Más simple es generalmente mejor. Además, nada en sus ejemplos es específico de GNU Sed; todos sus comandos usan las características POSIX estándar de Sed .
Comodín el
1

Puede usar diffpara comparar la salida de head/ tailcon el archivo original y luego eliminar lo que es lo mismo, por lo tanto, obtener el inverso.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
sokoban
fuente
1

Mientras tail -n +4que el archivo de salida que comienza en la cuarta línea (todas menos las primeras 3 líneas) es estándar y portátil, su headcontraparte ( head -n -3todas menos las últimas 3 líneas) no lo es.

Portablemente, harías:

sed '$d' | sed '$d' | sed '$d'

O:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(Tenga en cuenta que en algunos sistemas donde sedtiene un espacio de patrón de tamaño limitado, que no escala a valores grandes de n).

O:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
Stéphane Chazelas
fuente
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Si se <infiletrata de un lseek()archivo normal y habilitable, sí, por supuesto, siéntase libre. Lo anterior es una construcción totalmente compatible con POSIX.

mikeserv
fuente
0

Mi enfoque es similar al de Gilles, pero en cambio solo invierto el archivo con cat y canalizo eso con el comando head.

tac -r thefile.txt | head thisfile.txt (reemplaza archivos)

Abe
fuente
0

Espero haber entendido claramente tu necesidad.

Tiene varias formas de completar su solicitud:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Donde / etc / passwd es su archivo

La segunda solución puede ser útil si tiene un archivo enorme:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Mathieu Coavoux
fuente
0

Solución para BSD (macOS):

Eliminar las primeras 2 líneas:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Eliminar las últimas 2 líneas:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... no es muy elegante pero hace el trabajo!

espectro
fuente