web-dev-qa-db-de.com

Wie kann ich die Reihenfolge der Zeilen in einer Datei umkehren?

Ich möchte die Reihenfolge der Zeilen in einer Textdatei (oder stdin) umkehren und dabei den Inhalt jeder Zeile beibehalten.

Also beginnend mit:

foo
bar
baz

Ich möchte am Ende mit 

baz
bar
foo

Gibt es dafür ein Standard-UNIX-Befehlszeilenprogramm?

532
Scotty Allen

BSD-Schwanz:

tail -r myfile.txt

Referenz: FreeBSD , NetBSD , OpenBSD und OS X Handbuchseiten.

383
Jason Cohen

Erwähnenswert ist auch: tac (das, ähm, die Umkehrung von cat). Teil von coreutils .

Eine Datei in eine andere umwandeln

tac a.txt > b.txt
1217

Es gibt die bekannten Sed-Tricks :

# 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

(Erläuterung: Nicht initiale Zeile wird vorangestellt, um Puffer, Swap-Zeile und Puffer zu halten, Zeile am Ende auszudrucken.)

Alternativ (bei schnellerer Ausführung) aus den awk-Einlagen :

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

Wenn Sie sich nicht daran erinnern können,

Perl -e 'print reverse <>'

Auf einem System mit GNU -Dienstprogrammen sind die anderen Antworten einfacher, aber nicht die ganze Welt ist GNU/Linux ...

141
ephemient

Wenn Sie in vim sind, verwenden Sie

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

Versuchen Sie tac, das unter Linux funktioniert. Wenn dies nicht funktioniert, verwenden Sie tail -r, das unter BSD und OSX funktioniert.

39
DigitalRoss
tac <file_name>

beispiel:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1
34
jins

Versuchen Sie den folgenden Befehl:

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

am Ende deines Befehls setze: | tac

tac macht genau das, wonach Sie fragen, es "Schreibe jede Datei in die Standardausgabe, letzte Zeile zuerst."

tac ist das gegenteil von cat :-).

17

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
15
konsolebox

Die einfachste Methode ist die Verwendung des Befehls tac. tac ist cat invers . Beispiel:

$ 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 
11
Yekatandilburg

Die Antwort " tail -r " gefällt mir wirklich gut, aber meine Lieblingsgawk-Antwort ist ....

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

EDIT Folgendes generiert eine zufällig sortierte Liste von Zahlen von 1 bis 10:

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

wo Punkte durch einen tatsächlichen Befehl ersetzt werden, der die Liste umkehrt

tac

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

python: mit [:: - 1] für 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]))")
2

Für eine Cross-OS-Lösung (d. H. OSX, Linux), die tac innerhalb eines Shell-Skripts verwenden kann, verwenden Sie homebrew, wie andere bereits erwähnt haben.

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

Dies funktioniert sowohl bei BSD als auch bei GNU.

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

Beste Lösung:

tail -n20 file.txt | tac
2
ITNM

Wenn Sie die Datei direkt ändern möchten, können Sie sie ausführen

sed -i '1!G;h;$!d' filename

Dadurch entfällt die Notwendigkeit, eine temporäre Datei zu erstellen und anschließend das Original zu löschen oder umzubenennen. Dies hat dasselbe Ergebnis. Zum Beispiel:

$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$

Basierend auf der Antwort von ephemient , die fast, aber nicht ganz das tat, was ich wollte.

1
Mark Booth

tail -r macht den Trick:

seq 1 20 | Schwanz -r

0
Bohdan

Ich sehe viele interessante Ideen. Aber versuche meine Idee. Fügen Sie Ihren Text ein:

rev | tr '\ n' '~' | rev | tr '~' '\ n'

dies setzt voraus, dass das Zeichen '~' nicht in der Datei enthalten ist. Dies sollte auf jeder UNIX-Shell ab 1961 funktionieren. Oder so ähnlich.

0
driver

Für Emacs-Benutzer: C-x h (gesamte Datei auswählen) und dann M-x reverse-region. Funktioniert auch, wenn Sie nur Teile oder Linien auswählen und diese zurücksetzen. 

0
Marius Hofert

Ich hatte die gleiche Frage, aber ich wollte auch, dass die erste Zeile (Kopfzeile) oben bleibt. Also musste ich die Kraft von awk nutzen

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 funktioniert auch in Cygwin oder Gitbash

0
KIC

Sie können dies mit vimstdin und stdout tun. Sie können auch ex verwenden, um POSIX-kompatibel zu sein. vim ist nur der visuelle Modus für ex. Tatsächlich können Sie ex mit vim -e Oder vim -E Verwenden (verbesserter ex - Modus). vim ist nützlich, da es im Gegensatz zu Tools wie sed die Datei zum Bearbeiten puffert, während sed für Streams verwendet wird. Möglicherweise können Sie awk verwenden, müssen jedoch alles in einer Variablen manuell puffern.

Die Idee ist, Folgendes zu tun:

  1. Lesen Sie von stdin
  2. Verschieben Sie es für jede Zeile in Zeile 1 (umzukehren). Befehl ist g/^/m0. Dies bedeutet global für jede Zeile g; stimmen Sie mit dem Anfang der Zeile überein, der mit irgendetwas übereinstimmt ^; Verschieben Sie es nach der Adresse 0 (Zeile 1) m0.
  3. Alles drucken. Befehl ist %p. Dies bedeutet für den Bereich aller Zeilen %; Gib die Zeile p aus.
  4. Zwangsweise beenden, ohne die Datei zu speichern. Befehl ist q!. Dies bedeutet, dass Sie q beenden. mit Nachdruck !.
# 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

Wie man dies wiederverwendbar macht

Ich benutze ein Skript, das ich ved nenne (VIM-Editor wie sed), um VIM zum Bearbeiten von stdin zu verwenden. Fügen Sie dies zu einer Datei mit dem Namen ved in Ihrem Pfad hinzu:

#!/usr/bin/env sh

vim - --not-a-term -Es "[email protected]" +'%p | q!'

Ich verwende einen + - Befehl anstelle von +'%p' +'q!', Weil vim Sie auf 10 Befehle beschränkt. Das Zusammenführen dieser Befehle ermöglicht es dem "[email protected]", 9 + - Befehle anstelle von 8 zu haben.

Dann können Sie tun:

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

Wenn Sie nicht über vim 8 verfügen, geben Sie stattdessen Folgendes in ved ein:

#!/usr/bin/env sh

vim -E "[email protected]" +'%p | q!' /dev/stdin
0
dosentmatter