¿Cómo puedo invertir el orden de las líneas en un archivo?

Me gustaría invertir el orden de las líneas en un archivo de texto (o stdin), conservando el contenido de cada línea.

Entonces, es decir, comenzando con:

foo bar baz 

Me gustaría terminar con

 baz bar foo 

¿Existe una utilidad de línea de comandos UNIX estándar para esto?

BSD cola:

 tail -r myfile.txt 

Referencia: páginas de manual de FreeBSD , NetBSD , OpenBSD y OS X.

También vale la pena mencionar: tac (el, ejem, reverso del cat ). Parte de coreutils .

Voltear un archivo en otro

 tac a.txt > b.txt 

Están los conocidos trucos de sed :

 # reverse order of lines (emulates "tac") # bug/feature in HHsed v1.5 causes blank lines to be deleted sed '1!G;h;$!d' # method 1 sed -n '1!G;h;$p' # method 2 

(Explicación: anteponer una línea no inicial para contener el búfer, cambiar la línea y mantener el búfer, imprimir la línea al final)

Alternativamente (con una ejecución más rápida) de awk one-liners :

 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file* 

Si no puedes recordar eso,

 perl -e 'print reverse <>' 

En un sistema con utilidades GNU, las otras respuestas son más simples, pero no todo el mundo es GNU / Linux …

Si estás en vim uso

 :g/^/m0 
 $ (tac 2> /dev/null || tail -r) 

Pruebe tac , que funciona en Linux, y si eso no funciona use tail -r , que funciona en BSD y OSX.

 tac  

ejemplo:

 $ cat file1.txt 1 2 3 4 5 $ tac file1.txt 5 4 3 2 1 

Pruebe el siguiente comando:

 grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }" 

Just Bash 🙂 (4.0+)

 function print_reversed { local lines i readarray -t lines for (( i = ${#lines[@]}; i--; )); do printf '%s\n' "${lines[i]}" done } print_reversed < file 

El método más simple es usar el comando tac . tac es el cat inverso. Ejemplo:

 $ cat order.txt roger shah armin van buuren fpga vhdl arduino c++ java gridgain $ tac order.txt > inverted_file.txt $ cat inverted_file.txt fpga vhdl arduino c++ java gridgain armin van buuren roger shah 

Me gusta mucho la respuesta ” tail -r “, pero mi respuesta gawk favorita es …

 gawk '{ L[n++] = $0 } END { while(n--) print L[n] }' file 

Mejor solución:

 tail -n20 file.txt | tac 

EDITAR lo siguiente genera una lista ordenada aleatoriamente de números del 1 al 10:

 seq 1 10 | sort -R | tee /tmp/lst |cat < (cat /tmp/lst) <(echo '-------') **...** 

donde los puntos se reemplazan con el comando real que invierte la lista

tac

 seq 1 10 | sort -R | tee /tmp/lst |cat < (cat /tmp/lst) <(echo '-------') \ <(tac) 

python: utilizando [:: - 1] en sys.stdin

 seq 1 10 | sort -R | tee /tmp/lst |cat < (cat /tmp/lst) <(echo '-------') \ <(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))") 

Para la solución de sistema operativo cruzado (es decir, OSX, Linux) que puede usar tac dentro de un script de shell use homebrew como otros lo han mencionado anteriormente, entonces solo alias tac como tal:

 brew install coreutils echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases) source ~/.bash_aliases tac myfile.txt 

Esto funcionará tanto en BSD como en GNU.

 awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename 

al final de tu comando pon: | tac | tac

Tenía la misma pregunta, pero también quería que la primera línea (encabezado) se mantuviera en la parte superior. Así que necesitaba usar el poder de awk

 cat dax-weekly.csv | awk '1 { last = NR; line[last] = $0; } END { print line[1]; for (i = last; i > 1; i--) { print line[i]; } }' 

PD también funciona en cygwin o gitbash

Puedes hacerlo con vim stdin y stdout . También puede usar ex para que sea compatible con POSIX . vim es solo el modo visual para ex . De hecho, puede usar ex con vim -e o vim -E (modo ex mejorado). vim es útil porque a diferencia de las herramientas como sed , almacena el archivo para su edición, mientras que sed se usa para las transmisiones. Es posible que pueda usar awk , pero debería almacenar manualmente todo en una variable.

La idea es hacer lo siguiente:

  1. Leer de stdin
  2. Para cada línea muévela a la línea 1 (hacia atrás). El comando es g/^/m0 . Esto significa globalmente, para cada línea g ; coincide con el inicio de la línea, que coincide con cualquier cosa ^ ; muévelo después de la dirección 0, que es la línea 1 m0 .
  3. Imprime todo. El comando es %p . Esto significa para el rango de todas las líneas % ; imprima la línea p .
  4. Salir con fuerza sin guardar el archivo. El comando es q! . Esto significa dejar q ; ! fuerza ! .
 # Generate a newline delimited sequence of 1 to 10 $ seq 10 1 2 3 4 5 6 7 8 9 10 # Use - to read from stdin. # vim has a delay and annoying 'Vim: Reading from stdin...' output # if you use - to read from stdin. Use --not-a-term to hide output. # --not-a-term requires vim 8.0.1308 (Nov 2017) # Use -E for improved ex mode. -e would work here too since I'm not # using any improved ex mode features. # each of the commands I explained above are specified with a + sign # and are run sequentially. $ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!' 10 9 8 7 6 5 4 3 2 1 # non improved ex mode works here too, -e. $ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!' # If you don't have --not-a-term, use /dev/stdin seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin # POSIX compliant (maybe) # POSIX compliant ex doesn't allow using + sign to specify commands. # It also might not allow running multiple commands sequentially. # The docs say "Implementations may support more than a single -c" # If yours does support multiple -c $ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin # If not, you can chain them with the bar, |. This is same as shell # piping. It's more like shell semi-colon, ;. # The g command consumes the |, so you can use execute to prevent that. # Not sure if execute and | is POSIX compliant. seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin 

Cómo hacer esto reutilizable

Utilizo un script que llamo ved (editor vim como sed ) para usar vim para editar stdin . Agregue esto a un archivo llamado ved en su camino:

 #!/usr/bin/env sh vim - --not-a-term -Es "$@" +'%p | q!' 

Estoy usando un comando + lugar de +'%p' +'q!' , porque vim te limita a 10 comandos. Así que fusionarlos permite que "$@" tenga 9 + comandos en lugar de 8.

Entonces puedes hacer:

 seq 10 | ved +'g/^/m0' 

Si no tiene vim 8, ponga esto en ved en su lugar:

 #!/usr/bin/env sh vim -E "$@" +'%p | q!' /dev/stdin 

cola -r hace el truco:

seq 1 20 | cola -r

Usando Visual Studio ™ puede seleccionar la línea inferior con el mouse. ‘cortar’ esta línea y luego ‘pegarla’ en la parte superior. Continúe haciendo esto hasta que la línea superior original esté en la parte inferior. Creo que este método funciona en otros editores, pero no lo he probado.

 sort -r < filename 

o

 rev < filename