Come posso stampare le righe dal file indietro (senza usare "tac")?


Risposte:


33

Usando sedper emulare tac:

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

3
Questa è una buona soluzione, ma alcune spiegazioni su come funziona saranno ancora migliori.
Chen Levy,

10
È un famoso sedone-liner. Vedi "36. Ordine inverso delle linee (emula" tac "comando Unix)." nel famoso Sed One-Liners spiegato per una spiegazione completa di come funziona.
Johnsyweb,

6
Forse vale la pena notare: "Questi due one-liner in realtà usano molta memoria perché mantengono l'intero file nel buffer di attesa in ordine inverso prima di stamparlo. Evitare questi one-liner per file di grandi dimensioni."
Mr. Shickadance,

Quindi fai tutte le altre risposte (tranne forse quella che usa sort- c'è la possibilità che userà un file temporaneo).
Casuale 832,

1
Si noti che tac è più veloce per i file normali perché legge il file al contrario. Per le pipe, deve fare lo stesso delle altre soluzioni (conservare in memoria o nei file temporanei), quindi non è significativamente più veloce.
Stéphane Chazelas,


6

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

tramite awk one fodere


4
Uno più corto:awk 'a=$0RS a{}END{printf a}'
ninjalj,

@ninjalj: potrebbe essere più breve, ma diventa estremamente lento quando le dimensioni del file aumentano . Ho rinunciato dopo aver aspettato 2 minuti e 30 secondi. but your first perl reverse <> `è la risposta migliore / più veloce sulla pagina (per me), a 10 volte più veloce di questa awkrisposta (tutti gli awk anseres sono più o meno gli stessi, nel tempo)
Peter.O

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

5

Come hai chiesto di farlo in bash, ecco una soluzione che non utilizza awk, sed o perl, solo una funzione bash:

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

L'output di

echo 'a
b
c
d
' | reverse

è

d
c
b
a

Come previsto.

Ma attenzione che le righe sono memorizzate, una riga in ciascuna ricorsivamente chiamata istanza della funzione. Così attento con file di grandi dimensioni.


1
Diventa rapidamente impraticabilmente lento all'aumentare della dimensione del file, rispetto anche alla più lenta delle altre risposte, e come hai suggerito, fa esplodere la memoria abbastanza facilmente: * bash funzione ricorsiva ... ma è un'idea interessante. Errore di segmentazione *
Peter.O

4

Puoi passare attraverso:

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

Il awkprefisso indica ciascuna riga con il numero di riga seguito da uno spazio. La sortinverte l'ordine delle linee di classificare il primo campo (numero di riga) in ordine inverso, stile numerico. E le sedstrisce dai numeri di riga.

L'esempio seguente mostra questo in azione:

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'

Emette:

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

Ok. Grazie! Ci proverò. E grazie per i commenti!

5
cat -nsi comporta in modo molto simile aawk '{print NR" "$0}'
Chen Levy,

2
Penso che si chiama trasformazione di Schwartz , o decora-decora-undecorate
ninjalj

Questo approccio generale è utile in questo tipo di handle che utilizza i file su disco se l'attività è troppo grande per essere ragionevolmente eseguita in memoria. Potrebbe essere più delicato in memoria, tuttavia se si utilizzavano file temporanei tra i passaggi anziché le pipe.
mc0e

1

In perl:

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

2
o il più cortoperl -e 'print reverse<>'
ninjalj

2
(In realtà, c'è uno più corto, ma è davvero brutto, testimone la sua terribilità: perl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: veloce, ma perde la prima riga ... @ ninjalj: reverse<)è veloce: buono! ma quello "veramente brutto" è estremamente lento all'aumentare del numero di righe. !! ....
Peter.O

@ Peter.O -nera superfluo lì, grazie.
Frederik Deweerdt,

La cosa bella di questo è che il contenuto del file non viene necessariamente letto in memoria (tranne forse in blocchi di sort).
Kusalananda

0

Soluzione solo BASH

leggi il file nell'array bash (una riga = un elemento dell'array) e stampa l'array nell'ordine inverso:

i=0 

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


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

Provalo con linee rientrate ... la versione di Philfr è un po 'migliore ma è comunque davvero incredibile, davvero, quando si tratta di elaborare il testo, non usarlo mai while..read.
don_crissti,

Utilizzare IFS=''e read -rper evitare che tutti i tipi di fughe e la rimozione di IFS finale possano rovinarlo. Penso che il bash mapfile ARRAY_NAMEbuiltin sia una soluzione migliore per la lettura negli array.
Arthur2e5,

0

Bash, con mapfilemenzionato nei commenti a Fiximan, e in realtà una versione forse migliore:

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

Le sue prestazioni sono sostanzialmente paragonabili alla sedsoluzione e diventano più veloci al diminuire del numero di linee richieste.


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
  • Numerare le righe con nl
  • ordina in ordine inverso per numero
  • rimuovere i numeri con sed

come mostrato qui:

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

Risultato:

c
a
f
e

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.