Wie kann ich Zeilen aus einer Datei zurück drucken (ohne "tac" zu verwenden)?

Antworten:

33

Verwenden sed, um zu emulieren tac:

sed '1!G;h;$!d' ${inputfile}
Johnsyweb
quelle
3
Dies ist eine gute Lösung, aber einige Erklärungen, wie dies funktioniert, werden noch besser sein.
Chen Levy
10
Es ist ein berühmter sedEinzeiler. Siehe "36. Umgekehrte Reihenfolge der Zeilen (Unix-Befehl" tac "emulieren)." in Famous Sed One-Liners Erklärt für eine vollständige Erklärung, wie es funktioniert.
Johnsyweb
6
Vielleicht ist es erwähnenswert: "Diese beiden Einzeiler belegen tatsächlich viel Speicher, da sie die gesamte Datei in umgekehrter Reihenfolge im Haltepuffer halten, bevor sie ausgedruckt wird. Vermeiden Sie diese Einzeiler für große Dateien."
Mr. Shickadance
Tun Sie dies auch für alle anderen Antworten (außer für die, die verwendet wird sort- es besteht die Möglichkeit, dass eine temporäre Datei verwendet wird).
Random832
1
Beachten Sie, dass tac für reguläre Dateien schneller ist, da die Datei rückwärts gelesen wird. Für Pipes muss es dasselbe tun wie die anderen Lösungen (im Speicher oder in temporären Dateien halten), ist also nicht wesentlich schneller.
Stéphane Chazelas
8

Mit ed:

ed -s infile <<IN
g/^/m0
,p
q
IN

Wenn Sie auf BSD/ sind OSX(und hoffentlich bald auch auf GNU/ linuxwie es POSIX sein wird ):

tail -r infile
don_crissti
quelle
6

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

über awk one liner

Mark McKinstry
quelle
4
Ein kürzerer:awk 'a=$0RS a{}END{printf a}'
Ninjalj
@ninjalj: Es kann kürzer sein, aber es wird extrem langsam, wenn die Dateigröße größer wird. Ich gab auf, nachdem ich 2 Minuten 30 Sekunden gewartet hatte. but your first perl reverse <> `es ist die beste / schnellste Antwort auf der Seite (für mich), 10-mal schneller als diese awkAntwort (alle awk Anseres sind ungefähr gleich, zeitlich)
Peter.O
1
Oderawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Stéphane Chazelas
5

Hier ist eine Lösung, die awk, sed oder perl nicht verwendet, sondern nur eine bash-Funktion:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

Die Ausgabe von

echo 'a
b
c
d
' | reverse

ist

d
c
b
a

Wie erwartet.

Achten Sie jedoch darauf, dass die Zeilen im Speicher gespeichert sind, eine Zeile in jeder rekursiv aufgerufenen Instanz der Funktion. So vorsichtig mit großen Dateien.

philfr
quelle
1
Es wird schnell unpraktisch langsam, wenn die Dateigröße zunimmt, im Vergleich zu den langsamsten der anderen Antworten, und wie Sie bereits angedeutet haben, lässt es den Speicher ziemlich leicht durcheinander: * bash rekursive Funktion ... aber es ist eine interessante Idee. Segmentierungsfehler *
Peter.O
4

Sie können es durchpfeifen:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

Das awkPräfix jeder Zeile enthält die Zeilennummer, gefolgt von einem Leerzeichen. Die sortReihenfolge der Zeilen wird umgekehrt, indem das erste Feld (Zeilennummer) in umgekehrter Reihenfolge im numerischen Stil sortiert wird. Und dersed Streifen von den Zeilennummern.

Das folgende Beispiel zeigt dies in Aktion:

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

Es gibt aus:

l
k
j
i
h
g
f
e
d
c
b
a

quelle
Okay. Vielen Dank! Ich werde es versuchen. Und danke für die Kommentare!
5
cat -nwirkt ähnlich wieawk '{print NR" "$0}'
Chen Levy
2
Ich denke, das nennt man Schwartzsche Transformation oder dekorieren-sortieren-undekorieren
Ninjalj
Dieser allgemeine Ansatz ist insofern hilfreich, als Sortierhandles Dateien auf der Festplatte verwenden, wenn die Aufgabe zu groß ist, um im Arbeitsspeicher ausgeführt zu werden. Es ist jedoch möglicherweise sparsamer, wenn Sie temporäre Dateien zwischen den Schritten anstelle von Pipes verwenden.
mc0e
1

In Perl:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'
Frederik Deweerdt
quelle
2
oder je kürzerperl -e 'print reverse<>'
Ninjalj
2
(Eigentlich ist es eine kürzere, aber es ist wirklich hässlich, Zeuge seiner Schrecklichkeit: perl -pe '$\=$_.$\}{')
ninjalj
@ Frederik Deweerdt: Schnell, aber es verliert die erste Zeile ... @ ninjalj: reverse<)ist schnell: gut! Aber die "wirklich hässliche" ist extrem langsam, da die Anzahl der Zeilen erhöht. !! ....
Peter.O
@ Peter.O das -nwar da überflüssig, danke.
Frederik Deweerdt
Das Schöne an diesem ist, dass der Inhalt der Datei nicht unbedingt in den Speicher eingelesen wird (außer möglicherweise in Stücken von sort).
Kusalananda
0

Nur-BASH-Lösung

Lies die Datei in das Bash-Array (eine Zeile = ein Element des Arrays) und drucke das Array in umgekehrter Reihenfolge aus:

i=0 

while read line[$i] ; do
    i=$(($i+1))
done < FILE


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done
Fiximan
quelle
Versuchen Sie es mit eingerückten Zeilen ... Philfrs Version ist ein bisschen besser, aber immer noch sehr langsam, also wirklich, wenn es um Textverarbeitung geht, nie verwenden while..read.
don_crissti
Verwenden Sie IFS=''und read -r, um zu verhindern, dass alle Arten von Ausbrüchen und nachfolgende IFS-Entfernung es vermasseln. Ich denke, dass die Bash- mapfile ARRAY_NAMEFunktion eine bessere Lösung für das Einlesen in Arrays ist.
Arthur2e5
0

Bash, mit mapfile in Kommentaren erwähntem Fiximan, und eigentlich eine möglicherweise bessere Version:

# last [LINES=50]
_last_flush(){ BUF=("${BUF[@]:$(($1-LINES)):$1}"); } # flush the lines, can be slow.
last(){
  local LINES="${1:-10}" BUF
  ((LINES)) || return 2
  mapfile -C _last_flush -c $(( (LINES<5000) ? 5000 : LINES+5 )) BUF
  BUF=("${BUF[@]}") # Make sure the array subscripts make sence, can be slow.
  ((LINES="${#BUF[@]}" > LINES ? LINES : "${#BUF[@]}"))
  for ((i="${#BUF[@]}"; i>"${#BUF[@]}"-LINES; i--)); do echo -n "${BUF[i]}"; done
}

Die Leistung ist im Wesentlichen mit der der sedLösung vergleichbar und wird mit abnehmender Anzahl der angeforderten Leitungen schneller.

Arthur2e5
quelle
0
Simple do this with your file to sort the data in reverse order, and it should be unique.

sed -n '1h; 1d; G; h; $ p'

Bipul Kumar
quelle
0
  • Nummerieren Sie die Zeilen mit nl
  • In umgekehrter Reihenfolge nach Nummer sortieren
  • Entferne die Zahlen mit sed

wie hier gezeigt:

echo 'e
> f
> a
> c
> ' | nl | sort -nr | sed -r 's/^ *[0-9]+\t//'

Ergebnis:

c
a
f
e
Benutzer unbekannt
quelle
-3
awk '{print NR" "$0}' | sort -nr
Rockstar
quelle