Mueva las primeras N líneas de salida al final sin usar un archivo temporal

11

Imagine una salida de un comando como

44444
55555
11111
22222
33333

¿Cómo puedo tirar de las primeras N líneas (las dos primeras en el ejemplo anterior) y agregarlas al final, pero sin usar el archivo temporal (por lo que solo uso tuberías)?

11111
22222
33333
44444
55555

Algo en la línea de | sed -n '3,5p;1,2p'(que obviamente no funciona ya que sed no se preocupa por el orden de los comandos).

Peter Uhnak
fuente
2
¿Por qué no podemos usar un archivo temporal?
Braiam

Respuestas:

13

Simplemente copie esas líneas en el búfer de retención (luego bórrelas) y cuando esté en la última línea agregue el contenido del búfer de retención al espacio del patrón:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

Con gnu sedusted podría escribirlo como

some command | sed '1,NUMBER{H;1h;d;};$G'

Aquí hay otra forma con ol ' ed( rdirige la salida some commandal búfer de texto y luego malinea 1,NUMBERlas líneas después de la $última):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

Tenga en cuenta que, como se señaló, ambos fallarán si la salida tiene menos de NUMBER+1 líneas. Un enfoque más sólido sería ( gnu sedsintaxis):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

este solo elimina líneas en ese rango siempre que no sean la última línea ( $!d); de lo contrario, sobrescribe el espacio del patrón con el contenido del búfer de retención ( g) y luego quits (después de imprimir el espacio del patrón actual).

don_crissti
fuente
2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'también funciona de forma portátil (tenga en cuenta que algunas sedimplementaciones no pueden contener más de unos pocos kilobytes en el espacio de espera, por lo que NUMBER no puede ser demasiado grande allí)
Stéphane Chazelas
@ StéphaneChazelas - gracias por la entrada - Por lo general voy con un comando por línea, ya que sé con certeza que es portátil - la sintaxis de múltiples expresiones siempre ha sido un poco confusa para mí, por ejemplo, los documentos posix dicen "El <right-brace> será precedido por un <newline> " así que según ellos, ¿no debería ser así sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
don_crissti
44
Típicamente, un nuevo -ereemplaza a una nueva línea. d;}Todavía no es POSIX pero es portátil. Eso se solucionará en la próxima especificación POSIX. Ver austingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas
2
@don_crissti gracias! Sería genial si también pudiera incluir una breve explicación de por qué funciona. (Por supuesto, iré a buscarlo, pero sería una respuesta más valiosa)
Peter Uhnak
En mi cabeza, lo inportable de 1,NUMBER{H;1h;d;}no tener un punto y coma inmediatamente después de la llave de apertura . Sin sedembargo, eso podría haber sido un error en SunOS 4.1 cuya solución aún está conectada a mis dedos 20 años después.
zwol
11

Un awkenfoque:

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

Un beneficio sobre el sedenfoque de @ don_crissti es que todavía funciona (genera las líneas) si la salida tiene nlíneas o menos.

Stéphane Chazelas
fuente
Es posible que desee reemplazar el hardcoded \ncon ORS, para que esto funcione con otros separadores de registros (digamos que desea usar párrafos, etc.).
fedorqui
6

Tengo xclipy con esto esto se puede hacer de esta manera:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

Aquí está su descripción:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)

fuente
3
+1 para el uso creativo (incorrecto) de xclip. La respuesta necesita un servidor X accesible / en ejecución.
jofel
3

Una manera Perl:

perl -ne '$.<3?($f.=$_):print;}{print $f'

O, lo mismo escrito menos crípticamente:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

Por ejemplo:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

Explicación

  • -ne: lea el archivo / flujo de entrada línea por línea y aplique el script dado por -ea cada línea.
  • $.<3: $.es el número de línea actual, así que cambie 3al número de líneas que desea desplazar.
  • $.<3?($f.=$_):print;: este es el operador condicional, el formato general es condition ? case1 : case2, se ejecutará case1si conditiones verdadero y case2si es falso. Aquí, si el número de línea actual es menor que 3, agrega la línea actual ( $_) a la variable $fy, si el número de línea es mayor que 3, se imprime.
  • }{ print $f: }{es la abreviatura de perl para END{}. Se ejecutará después de que se hayan procesado todas las líneas de entrada. En este punto, habremos reunido todas las líneas que queremos cambiar y habremos impreso todas las que queremos dejar solas, así que imprima las líneas guardadas como $f.
terdon
fuente
1
con respecto a su versión de golf, se pueden eliminar algunos caracteresperl -ne '$.<3?$f.=$_:print}{print $f
123
1

Utiliza POSIX ex. Sí, está destinado a la edición de archivos, pero funcionará en una tubería.

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

Esto puede tener cualquier comando arbitrario agregado al principio o al final de la tubería y funcionará igual. Mejor aún, dada la presencia de /dev/stdin, es compatible con POSIX.

(No sé si /dev/stdinse especifica en POSIX o no, pero veo que está presente tanto en Linux como en Mac OS X).

Esto tiene una ventaja de legibilidad sobre el uso seddel espacio de espera: simplemente dice ex"mover estas líneas hasta el final" y lo hace. (El resto de los comandos significa "imprimir el búfer" y "salir", que también son bastante legibles).

NB: El excomando dado anteriormente fallará si se le dan menos de 2 líneas como entrada.

Otras lecturas:

Comodín
fuente
0

Un pythonfragmento corto :

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

Pase el nombre del archivo como primer argumento y el número de líneas para moverse como segundo argumento.

Ejemplo:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222
heemayl
fuente
0

Si puede almacenar toda la salida en la memoria:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile
Glenn Jackman
fuente
0

Aquí hay otra opción que requiere GNU sed:

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-uhace que GNU no tenga sedbúfer para que el sedcomando no consuma más de 3 líneas de STDIN. La sustitución del comando elimina las líneas vacías, de modo que el comando no incluye líneas vacías al final de la salida si las líneas tercera, tercera y segunda, o tercera, segunda y primera están vacías.

También puedes hacer algo como esto:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

Sin sponge /dev/stdout|el comando fallaría con entradas largas. sponge /dev/stdouttambién se puede reemplazar con tac|tac, aunque eso se imprima, por ejemplo, a\ncbsi la entrada es a\nb\ncdonde \nhay un salto de línea, o con (x=$(cat);printf %s\\n "$x"), aunque eso elimine las líneas vacías del final de la entrada. El comando anterior elimina la primera línea cuando el número de líneas de la entrada es uno o dos.

nisetama
fuente