Jak mogę wydrukować linie z pliku do tyłu (bez użycia „tac”)?


Odpowiedzi:


33

Używanie seddo emulacji tac:

sed '1!G;h;$!d' ${inputfile}

3
To dobre rozwiązanie, ale wyjaśnienie, jak to działa, będzie jeszcze lepsze.
Chen Levy,

10
To słynny sedjednowarstwowy. Zobacz „36. Odwróć kolejność wierszy (emuluj polecenie„ tac ”Unix).” w Famous Sed One-Liners wyjaśnił pełne wyjaśnienie tego, jak to działa.
Johnsyweb 16.03.11

6
Być może warto zauważyć: „Te dwa jednowierszowe faktycznie zużywają dużo pamięci, ponieważ przechowują cały plik w buforze wstrzymania w odwrotnej kolejności przed wydrukowaniem. Unikaj tych jednowarstwowych dla dużych plików”.
Pan Shickadance,

Podobnie jak wszystkie pozostałe odpowiedzi (może z wyjątkiem jednej używającej sort- istnieje szansa, że ​​użyje pliku tymczasowego).
Random832

1
Zauważ, że tac jest szybszy dla zwykłych plików, ponieważ odczytuje plik do tyłu. W przypadku potoków musi działać tak samo, jak inne rozwiązania (przechowywanie w pamięci lub w plikach tymczasowych), więc nie jest znacznie szybszy.
Stéphane Chazelas,


6

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

przez awk one liner


4
Krótszy:awk 'a=$0RS a{}END{printf a}'
ninjalj 17.03.11

@ ninjalj: może być krótszy, ale staje się bardzo wolny, gdy rozmiar pliku staje się większy. Poddałem się po odczekaniu 2 minut 30 sekund. but your first perl reverse <> `to najlepsza / najszybsza odpowiedź na stronie (dla mnie), 10 razy szybsza niż ta awkodpowiedź (wszystkie ankresy awk są mniej więcej takie same, jeśli chodzi o czas)
Peter.O

1
Lubawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Stéphane Chazelas,

5

Gdy poprosiłeś o zrobienie tego w bashu, oto rozwiązanie, które nie korzysta z awk, sed ani perla, tylko funkcję bash:

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

Dane wyjściowe

echo 'a
b
c
d
' | reverse

jest

d
c
b
a

Zgodnie z oczekiwaniami.

Ale uwaga: wiersze są przechowywane w pamięci, po jednym wierszu w każdej rekurencyjnie nazywanej instancji funkcji. Ostrożnie z dużymi plikami.


1
Szybko staje się niepraktycznie wolny wraz ze wzrostem wielkości pliku, w porównaniu do nawet najwolniejszej z pozostałych odpowiedzi, i jak sugerujesz, dość łatwo wysadza pamięć: * funkcja rekurencyjna bash ... ale to ciekawy pomysł. Błąd segmentacji *
Peter.O,

4

Możesz przepuścić przez:

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

Te awkprefiksy każda linia z numerem linii a następnie po spacji. sortOdwraca się kolejność linii sortując na pierwszym polu (numer linii) w odwrotnej kolejności, stylu numerycznej. I sedusuwa numery linii.

Poniższy przykład pokazuje to w działaniu:

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'

Wyprowadza:

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

Dobrze. Dzięki! Spróbuję tego. I dzięki za komentarze!

5
cat -nzachowuje się bardzo podobnieawk '{print NR" "$0}'
Chen Levy

2
Myślę, że to się nazywa transformacja Schwartziana lub dekorowanie-sortowanie-undecorate
ninjalj

To ogólne podejście jest przyjemne w tym rodzaju, że obsługuje pliki na dysku, jeśli zadanie jest zbyt duże, aby można je było właściwie wykonać w pamięci. Pamięć może być łagodniejsza, jeśli użyjesz plików tymczasowych między krokami zamiast potoków.
mc0e,

1

W perlu:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

2
lub krótszyperl -e 'print reverse<>'
ninjalj 17.03.11

2
(Faktycznie, istnieje jeden krótszy, ale to jest naprawdę brzydka, świadkiem jego awfulness: perl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: Szybko, ale traci pierwszą linię ... @ ninjalj: reverse<)jest szybki: dobrze! ale „naprawdę brzydka” jest bardzo wolna, gdy liczba linii rośnie. !! ....
Peter.O

@ Peter.O to -nbyło zbyteczne, dzięki.
Frederik Deweerdt,

Zaletą tego jest to, że zawartość pliku niekoniecznie jest wczytywana do pamięci (z wyjątkiem być może fragmentów sort).
Kusalananda

0

Rozwiązanie tylko dla BASH

wczytaj plik do tablicy bash (jedna linia = jeden element tablicy) i wydrukuj tablicę w odwrotnej kolejności:

i=0 

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


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done

Wypróbuj z wciętymi liniami ... wersja philfr jest nieco lepsza, ale wciąż bardzo słaba, więc naprawdę, jeśli chodzi o przetwarzanie tekstu, nigdy nie używaj while..read.
don_crissti

Użyj IFS=''i, read -raby zapobiec wszelkim ucieczkom i końcowemu usuwaniu IFS przed zepsuciem go. Myślę, że mapfile ARRAY_NAMEwbudowane bash jest lepszym rozwiązaniem do wczytywania tablic.
Arthur2e5

0

Bash, z mapfilewymienionymi w komentarzach do fiximan, a właściwie być może lepszą wersją:

# 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
}

Jego wydajność jest zasadniczo porównywalna z sedrozwiązaniem i rośnie szybciej wraz ze spadkiem liczby żądanych linii.


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'


0
  • Numeruj linie za pomocą nl
  • sortuj w odwrotnej kolejności według numerów
  • usuń liczby za pomocą sed

jak pokazano tutaj:

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

Wynik:

c
a
f
e

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.