Como posso imprimir linhas de um arquivo para trás (sem usar "tac")?

26

Quero imprimir linhas de um arquivo para trás sem usar o taccomando Existe alguma outra solução para fazer isso com o bash?

jimmij
fonte

Respostas:

33

Usando sedpara emular tac:

sed '1!G;h;$!d' ${inputfile}
Johnsyweb
fonte
3
Esta é uma boa solução, mas algumas explicações sobre como isso funciona serão ainda melhores.
Chen Levy
10
É uma frase famosa sed. Consulte "36. Ordem inversa de linhas (emule o comando Unix" tac ")". em Famous Sed One-Liners Explained para uma explicação completa de como funciona.
Johnsyweb
6
Talvez valha a pena notar: "Esses dois liners na verdade usam muita memória porque mantêm o arquivo inteiro no buffer de espera na ordem inversa antes de imprimi-lo. Evite esses liners para arquivos grandes".
Shickadance
O mesmo acontece com todas as outras respostas (exceto, talvez, a que está sendo usada sort- há uma chance de que ele use um arquivo temporário).
usar o seguinte código
11
Observe que o tac é mais rápido para arquivos regulares, porque ele lê o arquivo para trás. Para pipes, ele deve fazer o mesmo que as outras soluções (armazenadas na memória ou em arquivos temporários), portanto, não é significativamente mais rápido.
Stéphane Chazelas
8

Com ed:

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

Se você estiver em BSD/ OSX(e espero que em breveGNU / linuxtambém como será o POSIX ):

tail -r infile
don_crissti
fonte
6

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

via awk one liners

Mark McKinstry
fonte
4
Um mais curto:awk 'a=$0RS a{}END{printf a}'
ninjalj 17/03/11
@ ninjalj: pode ser mais curto, mas fica extremamente lento à medida que o tamanho do arquivo aumenta. Desisti depois de esperar 2min 30seg. but your first Perl reverter <> `que a melhor resposta / mais rápido na página (para mim), a 10 vezes mais rápido do que este awkresposta (todos os anseres awk são sobre o mesmo, o tempo-wise)
Peter.O
11
Ouawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Stéphane Chazelas
5

Como você pediu no bash, eis uma solução que não utiliza awk, sed ou perl, apenas uma função do bash:

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

A saída de

echo 'a
b
c
d
' | reverse

é

d
c
b
a

Como esperado.

Mas tenha cuidado que as linhas são armazenadas na memória, uma linha em cada recursivamente chamada instância da função. Tome cuidado com arquivos grandes.

philfr
fonte
11
Ele rapidamente se torna impraticável- mente lento à medida que o tamanho do arquivo aumenta, comparado às respostas mais lentas, e como você sugeriu, ele sopra a memória com bastante facilidade: * bash função recursiva ... mas é uma idéia interessante. Falha na segmentação *
Peter.O
4

Você pode canalizá-lo através de:

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

O awkprefixo de cada linha com o número da linha seguido por um espaço. A sortinverte a ordem das linhas pela triagem no primeiro campo (número de linha) em ordem inversa, estilo numérico. E as sedtiras dos números de linha.

O exemplo a seguir mostra isso em ação:

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'

Emite:

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

fonte
Está bem. Obrigado! Eu vou tentar isso. E obrigado pelos comentários!
5
cat -nage comoawk '{print NR" "$0}'
Chen Levy
2
Eu acho que se chama Schwartziana transformar , ou decorar-sort-undecorate
ninjalj
Essa abordagem geral é boa nesse tipo de manipulação usando arquivos em disco se a tarefa for muito grande para ser razoavelmente executada na memória. Pode ser mais suave na memória, se você usou arquivos temporários entre as etapas, em vez de pipes.
Mc0e
1

Em perl:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'
Frederik Deweerdt
fonte
2
ou o mais curtoperl -e 'print reverse<>'
ninjalj 17/03/11
2
(Na verdade, há um mais curto, mas é realmente feio, testemunha o seu horror: perl -pe '$\=$_.$\}{')
ninjalj
@ Frederik Deweerdt: Rápido, mas perde a primeira linha ... @ ninjalj: reverse<)é rápido: bom! mas o "muito feio" é extremamente lento como o número de linhas aumenta !! .....
Peter.O
@ Peter.O o -nera supérfluo lá, obrigado.
Frederik Deweerdt
O bom desse exemplo é que o conteúdo do arquivo não é necessariamente lido na memória (exceto possivelmente em pedaços por sort).
Kusalananda
0

Solução somente BASH

leia o arquivo no array bash (uma linha = um elemento do array) e imprima o array na ordem inversa:

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
fonte
Experimente com linhas recuadas ... A versão do philfr é um pouco melhor, mas ainda muito lenta, na verdade, quando se trata de processamento de texto, nunca use while..read.
31415 don_crissti
Use IFS=''e read -rpara impedir que todos os tipos de fugas e remoção posterior do IFS estragem. Eu acho que o bash mapfile ARRAY_NAMEbuiltin é uma solução melhor para a leitura de matrizes.
Arthur2e5
0

Bash, mapfilemencionado nos comentários para fiximan, e na verdade uma versão possivelmente melhor:

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

Seu desempenho é basicamente comparável à sedsolução e fica mais rápido à medida que o número de linhas solicitadas diminui.

Arthur2e5
fonte
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
fonte
0
  • Numere as linhas com nl
  • ordenar em ordem inversa pelo número
  • remova os números com sed

como mostrado aqui:

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

Resultado:

c
a
f
e
Usuário desconhecido
fonte
-3
awk '{print NR" "$0}' | sort -nr
Estrela do rock
fonte