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:
- Leer de stdin
- Para cada línea, muévelo a la línea 1 (para invertir). El comando es
g/^/m0
. Esto significa globalmente, para cada líneag
; 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 1m0
. - Imprime todo. El comando es
%p
. Esto significa para el rango de todas las líneas%
; imprime la líneap
. - Salir forzosamente sin guardar el archivo. El comando es
q!
. Esto significa salirq
; 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
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