¿Cómo elimino las primeras n líneas y la última línea de un archivo usando comandos de shell?

31

Tengo un archivo llamado que Element_querycontiene el resultado de una consulta:

SQL> select count (*) from element;

[Output of the query which I want to keep in my file]

SQL> spool off;

Quiero eliminar la primera línea y la última línea usando el comando de shell.

pmaipmui
fuente
2
Probablemente sea mejor arreglar esto dentro de SQL * Plus; en lugar de generar un archivo y luego tratar de recortar las cosas que no desea, puede decirle a SQL * Plus que no genere esas cosas para empezar. Un enfoque se describe en la sección "Creación de un archivo plano" en docs.oracle.com/cd/A84870_01/doc/sqlplus.816/a75664/ch44.htm ; Se describe otro enfoque en stackoverflow.com/q/2299375/978917 .
ruakh

Respuestas:

48

Usando GNU sed:

sed -i '1d;$d' Element_query

Cómo funciona :

  • -iopción editar el archivo en sí. También puede eliminar esa opción y redirigir la salida a un nuevo archivo u otro comando si lo desea.
  • 1delimina la primera línea ( 1para actuar solo en la primera línea, dpara eliminarla)
  • $delimina la última línea ( $para actuar solo en la última línea, dpara eliminarla)

Ir más lejos :

  • También puede eliminar un rango. Por ejemplo, 1,5deliminaría las primeras 5 líneas.
  • También puede eliminar cada línea que comienza con el SQL>uso de la declaración/^SQL> /d
  • Puede eliminar cada línea en blanco con /^$/d
  • Finalmente, puede combinar cualquiera de las declaraciones separándolas con un punto y coma ( statement1;statement2;satement3;...) o especificándolas por separado en la línea de comando ( -e 'statement1' -e 'statement 2' ...)
user43791
fuente
Si es la tercera línea para eliminar ... ¿entonces tengo que usar 3d en lugar de 1d? si su tercera línea es la última en eliminarse, ¿cuál será el comando?
pmaipmui
¿Cómo eliminar la tercera línea de la última usando comandos de shell?
pmaipmui
@Nainita Puede especificar un rango ( 1,3deliminará las primeras tres líneas) pero es un poco más difícil para el final. Dependiendo de lo que desee, podría ser mejor usar esto: sed -i '/^SQL> /d' Element_querypara eliminar líneas que comienzan SQL> sin importar dónde se encuentre en el archivo.
user43791
@Nainita: vea mi respuesta aquí para los recuentos de cola arbitrarios: ofrece dos soluciones para eliminar las líneas de recuento en relación con el final del archivo. Uno es un sedtrazador de líneas, que funcionará para eliminar los recuentos de líneas arbitrarias del encabezado y la cola de un archivo. Sin embargo, siempre que la entrada sea un archivo normal, es solo agrupar una sola entrada en dos headprocesos: es el La forma más rápida de hacer esto por lo general.
mikeserv
Solía sed -i '1d' table-backup.sqleliminar la primera línea del archivo de texto SQL
David Thomas
8

cabeza; cabeza

{   head -n[num] >/dev/null
    head -n[num]
}  <infile >outfile

Con lo anterior, puede especificar el primer número de líneas para eliminar del encabezado de la salida con el primer headcomando, y el número de líneas para escribir outfilecon el segundo. Por lo general, también lo hará más rápido que sed, especialmente cuando la entrada es grande , a pesar de requerir dos invocaciones. Donde sedsin duda debe ser preferido, sin embargo, es en el caso de que <infilees no un habitual, lseekable archivo - porque esto normalmente no funciona como es debido, en ese caso, pero sedpuede manejar todas las modificaciones de salida en un solo proceso, con guión.

Con un GNU headtambién puede usar la -forma negativa [num]en el segundo comando. En cuyo caso, el siguiente comando eliminará la primera y última línea de la entrada:

{   head -n1 >/dev/null
    head -n-1
}  <infile >outfile

O con un POSIX sed:

Digamos, por ejemplo, que estaba leyendo una entrada de 20 líneas y quería eliminar las primeras 3 y las últimas 7. Si resolviera hacerlo con / sed, lo haría con un buffer de cola. Primero sumaría tres y siete para un recuento total de diez y luego hago:

seq 20 | sed -ne:n -e '3d;N;1,10bn' -eP\;D

Ese es un ejemplo que elimina las primeras 3 y las últimas 7 líneas de la entrada. La idea es que puede almacenar tantas líneas como desee de la cola de entrada en el espacio del patrón en una pila, pero solo Pborre la primera de estas para cada línea extraída.

  • En las líneas 1,10 sed Pno hay nada porque para cada una de ellas está apilando la entrada en el espacio del patrón línea por línea en un bbucle de rancho.
  • En la tercera línea, sedse delige toda la pila , por lo que las primeras 3 líneas se eliminan de la salida de una sola vez.
  • Cuando sedllega a la $última línea de entrada e intenta extraer la Nextensión, golpea EOF y detiene el procesamiento por completo. Pero en ese momento el espacio del patrón contiene todas las líneas 14,20, ninguna de las cuales ha sido Pborrada y nunca lo ha sido.
  • En cualquier otra línea, se sed Palinea solo hasta la primera línea de flujo que ocurre \nen el espacio del patrón, y Delige lo mismo antes de comenzar un nuevo ciclo con lo que queda, o las siguientes 6 líneas de entrada. La séptima línea se agrega nuevamente a la pila con el Ncomando ext en el nuevo ciclo.

Y así, de seqla salida de (que es 20 líneas numeradas secuencialmente) , sedsolo imprime:

4
5
6
7
8
9
10
11
12
13

Esto se vuelve problemático cuando el número de líneas que desea eliminar de la cola de entrada es grande, porque sedel rendimiento es directamente proporcional al tamaño de su espacio de patrón. Aún así, sin embargo, es una solución viable en muchos casos, y POSIX especifica un sedespacio de patrón para manejar al menos 4 kb antes de reventar.

mikeserv
fuente
1
gnu tailtambién admite la tail -n+<num>sintaxis extendida que significa "comenzar desde la línea <num>"
UloPe
4

No voy a responder cómo eliminar varias líneas. Voy a atacar el problema de esta manera:

grep -v '#SQL>' Element_query >outfile

En lugar de contar líneas, elimina los comandos SQL al reconocer las solicitudes. Esta solución se puede generalizar para otros archivos de salida de sesiones SQL con más comandos que solo dos.

Monty Harder
fuente
Me gusta. No sé mucho sobre SQL, pero ¿no existe la posibilidad de que las indicaciones ocurran en la cabecera de sus líneas de salida?
mikeserv
4

edes 'el editor de texto estándar' y debería estar disponible en sistemas que no tienen GNU sed. Originalmente fue diseñado como un editor de texto, pero se adapta bien a los scripts.

printf '%s\n' 1d '$d' w q | ed Element_query

1delimina la primera línea del archivo, $d(citado para que el shell no piense que es una variable) elimina la última línea, wescribe el archivo y se qcierra ed. printfaquí se usa para formatear los comandos para ed: cada uno debe ir seguido de una nueva línea; Por supuesto, hay otras formas de lograr esto.

maldad
fuente
3

Hay varias formas de eliminar las líneas iniciales y finales de un archivo.

Puede usarlo awkya que maneja la coincidencia de patrones y el recuento de líneas,

#you need to know length to skip last line, assume we have 100 lines
awk 'NR>1 && NR<100 {print $0};' < inputfile
#or
awk '/SQL/ {next}; {print $0;};' < inputfile |awk 'NR>1&& NR<10 {print $0};'

Puede usar grep -vpara excluir líneas que no desea por patrón, y puede hacer coincidir múltiples patrones usando la -Eopción,

grep -v -E "SQL>" < inputfile > outputfile

Puede usar heady tailpara recortar recuentos específicos de líneas,

lines=$((`wc -l < inputfile`-2)) #how many lines in file, less 2
head -n-1 < inputfile | head -n$lines > outputfile
#or
tail -n+2 < inputfile | head -n$lines > outputfile

Puede usar vi/vimy eliminar la primera y la última línea (s),

vi inputfile
:1
dd
:$
dd
:w! outputfile
:x

podría usar un script en perl, omitir la primera línea, guardar cada línea, imprimir cuando obtenga una línea siguiente,

#left as exercise for the reader :-)
ChuckCottrill
fuente
1
En headrealidad, no necesita la tubería y, de hecho, es mejor no usarla si puede salirse con la suya. Cuando lo haga head | head, aunque los dos procesos pueden ejecutarse simultáneamente, ambos procesan prácticamente todos los mismos datos de forma redundante. Si lo hace { head >dump; head >save; } <in, solo salta por desplazamiento: el primero lee 10 líneas >dumpy el segundo lee las siguientes 10 líneas >save.
mikeserv
3

Sería mucho mejor servir cortando los comandos SQL. Puede hacer esto de dos maneras:

  1. Si está absolutamente seguro de que la secuencia " SQL>" no aparece en ningún otro lugar de la salida,

    grep -v -F 'SQL> ' < infile > outfile
  2. Si no estás tan seguro,

    grep -v '^SQL> .*;$' < infile > outfile

La segunda versión es más lenta pero más precisa: ignorará las líneas que comienzan exactamente con "SQL>" y terminan en punto y coma, que parecen describir las líneas que desea eliminar.

Sin embargo, sería mejor no incluir esa salida adicional en el archivo para empezar. La mayoría de los sistemas SQL tienen alguna forma de hacerlo. No estoy muy familiarizado con Oracle, pero tal vez esta respuesta podría ser útil.

LSerni
fuente
3

Puede seleccionar las líneas entre un rango en awk(esto supone que sabe cuántas líneas hay):

awk 'NR>1 && NR < 3' file

O en Perl:

perl -ne 'print if $.>1 && $.<3' file

Si no sabe cuántas líneas hay, puede calcularlas sobre la marcha usando grep(tenga en cuenta que esto no contará las líneas en blanco, use grep -c '' filepara contarlas también):

awk -vm="$(grep -c . file2.txt)" 'NR>1 && NR<m' file2.txt
terdon
fuente
3

Prueba esta solución:

tail -n +2 name_of_file | head -n-1

Personalización

Puede adaptarlo fácilmente para eliminar las n primeras líneas cambiando el +2de tail;
o para eliminar las últimas n líneas cambiando el -1de head.

Gabrer
fuente
Esta solución es incorrecta ya que imprime la primera línea.
xhienne
1
@xhienne Lo siento, fue un error. Escribí 1 en lugar de 2 como el parámetro de "cola". Ahora funciona, gracias! :)
Gabrer
1

Utilizando awk:

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile: redirige el contenido de inputfileto awk'sstdin
  • > outputfile: redirige el contenido de awk's stdoutaoutputfile
  • NR>1: ejecuta las siguientes acciones solo si el número del registro que se procesa es mayor que 1
  • {print r}: imprime el contenido de la variable r
  • {r=$0}: asigna el contenido del registro que se procesa a la variable r

Entonces, en la primera ejecución del awkscript, el primer bloque de acciones no se ejecuta, mientras que el segundo bloque de acciones se ejecuta y el contenido del registro se asigna a la variable r; en la segunda ejecución, se ejecuta el primer bloque de acciones y rse imprime el contenido de la variable (por lo que se imprime el registro anterior); Esto tiene el efecto de imprimir cada línea procesada pero la primera y la última.

kos
fuente
No está excluyendo la primera línea. En NR == 2, imprime la primera línea de entrada que se almacena en r.
xhienne