the synopsis script learning ejemplos displays commands built shell unix command-line

shell - synopsis - the linux command line epub



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

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

Entonces, es decir, comenzando con:

foo bar baz

Me gustaría terminar con

baz bar foo

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


Ahí están los trucos sed conocidos :

# 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 línea no inicial para mantener el búfer, intercambiar línea y mantener el búfer, imprimir la línea al final)

Alternativamente (con una ejecución más rápida) desde los 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 ...



El método más simple es usar el comando tac . tac es la inversa del cat . 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


Esto funcionará tanto en BSD como en GNU.

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


Mejor solución:

tail -n20 file.txt | tac


Para la solución de sistemas operativos cruzados (es decir, OSX, Linux) que puede usar tac dentro de un script de shell, use homebrew como otros han mencionado anteriormente, luego simplemente alias tac como tal:

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


Pruebe el siguiente comando:

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


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

La idea es hacer lo siguiente:

  1. Leer de stdin
  2. Para cada línea, muévelo a la línea 1 (para invertir). 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évalo 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 % ; imprime la línea p .
  4. Salir forzosamente sin guardar el archivo. El comando es q! . Esto significa salir q ; con 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

Como hacer este reutilizable.

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

#!/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. Por lo tanto, combinarlos 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


Realmente me gusta la respuesta " tail -r ", pero mi respuesta favorita es ...

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


Sólo 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


Si usted está en uso vim

:g/^/m0


También vale la pena mencionar: tac (the, ejem, reverse of cat ). Parte de los coreutils .

Volteando un archivo a otro

tac a.txt > b.txt


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]; } }''

PS también funciona en cygwin o gitbash.


Usando Visual Studio ™ puede seleccionar la línea inferior con el mouse. ''cortar'' esta línea y luego ''pegarla'' en la parte superior. Continúa 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.


al final de su comando poner: | tac | tac

tac hace exactamente lo que estás pidiendo, "escribe cada ARCHIVO en la salida estándar, la última línea primero".

tac es lo contrario de cat :-).


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: usando [:: - 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]))")


cola -r hace el truco

seq 1 20 | cola -r


$ (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.


sort -r < filename

o

rev < filename


tac <file_name>

ejemplo:

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