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?
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 ...
Wenn Sie in vim
sind, verwenden Sie
:g/^/m0
$ (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.
tac <file_name>
beispiel:
$ cat file1.txt
1
2
3
4
5
$ tac file1.txt
5
4
3
2
1
Versuchen Sie den folgenden Befehl:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
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 :-).
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
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
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
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]))")
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
Dies funktioniert sowohl bei BSD als auch bei GNU.
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
Beste Lösung:
tail -n20 file.txt | tac
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.
tail -r macht den Trick:
seq 1 20 | Schwanz -r
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.
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.
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
Sie können dies mit vim
stdin
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:
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
.%p
. Dies bedeutet für den Bereich aller Zeilen %
; Gib die Zeile p
aus.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