¿Cómo imprimo todo menos el enésimo hasta la última línea en sed?

9
  • Me gustaría hacer el complemento / "opuesto" de

    sed 13q;d <file.txt
    

    En términos más generales, ¿es posible hacer este tipo de complemento / inverso / opuesto en sed? ¿O solo para expresiones regulares?

  • ¿Cómo imprimo todo excepto la penúltima línea? ¿Requiere esto dos tacy contando hacia adelante sed? ¿O hay una manera sedde contar desde atrás?

isomorfismos
fuente

Respuestas:

12

Parte 1

Simplemente delija la línea 13:

sed '13d' <file.txt

Y una forma general de hacer el complemento de lo anterior es:

sed '13!d' <file.txt

Parte 2

Porque se puede hacer:

sed -n ':a;${P;q};N;4,$D;ba' <file.txt

Tenga en cuenta que 4es uno más que el número que necesita. Entonces, si quisieras la última línea 10, esta sería 11.

Prueba con seq:

$ seq 100 | sed -n ':a;${P;q};N;4,$D;ba'
98
$ 

Intento de explicación

:a        # define label a
${        # match the last line
    P     # print the first line of the pattern space
    q     # quit
}
N         # match all lines: append the next line to the pattern
4,${      # match the range of lines 4 to the end of the file
    D     # delete the first line of the pattern space
}
ba        # match all lines: jump back to label a 

La valiosa adición de Glenn Jackman:

Esa fue "solo la enésima línea". Aquí está "todo PERO la enésima línea":

sed -n ':a;${s/^[^\n]*\n//;p;q};N;4,${P;D};ba'

funciona con GNU sed, la \nsecuencia puede no funcionar con otros seds.


Intenté esto con BSD sed (OSX) y descubrí que no funcionaba en la forma anterior. Los problemas parecen ser:

  1. ; usado para separar líneas parece funcionar generalmente, pero no funciona después de una etiqueta
  2. BSD sed parece requerir ;después del último comando en un {}grupo de comandos de una línea , mientras que GNU sed no
  3. \ngeneralmente se puede usar dentro de la expresión regular, pero aparentemente no dentro de una []expresión de paréntesis. Entonces, para excluir nuevas líneas, podemos usar algo como [[:alnum:][:punct:][:graph:][:blank:]], en cambio, esto puede excluir otros caracteres (específicamente otros caracteres de control).

Entonces este es un intento de una versión más independiente de la plataforma:

sed -n ':a
${s/^[[:alnum:][:punct:][:graph:][:blank:]]*\n//p;q;};N;4,${P;D;};ba'

Esto parece funcionar bajo OSX y Ubuntu.

Trauma digital
fuente
@jimmij Las otras respuestas sobre preguntas relacionadas en la red SE sugieren que una solución head/ tailes mucho más lenta que una sedsolución. Gracias sin embargo.
isomorphismes
3
@isomorphismes ningún programa puede conocer el número de líneas en un archivo a menos que atraviese todo el archivo. No hay forma de evitar eso. La única forma de contar desde abajo es invertir el archivo y contar desde arriba o analizarlo dos veces. Entonces la cabeza / cola van a ser lo más rápido posible.
terdon
@isomorphismes ... porque ( head/ tail) están optimizados para hacer lo que hacen.
Peter
@isomorphismes - editado con todas las partes que necesita
Digital Trauma
¡Agradable! Tuve que cambiar mi respuesta, ya que de alguna manera esperaba que fuera más complicado. :)
Peter