Como posso imprimir linhas do arquivo de trás para frente (sem usar “tac”)?

23

Eu quero imprimir as linhas de um arquivo para trás sem usar o comando tac . Existe alguma outra solução para fazer tal coisa com bash?

    
por jimmij 16.03.2011 / 11:51

10 respostas

27

Usando sed para emular tac :

sed '1!G;h;$!d' ${inputfile}
    
por 16.03.2011 / 11:59
7

com ed :

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

Se você estiver em BSD / OSX (e esperamos que em breve em GNU / linux também como será POSIX ):

tail -r infile
    
por 29.07.2015 / 01:15
6

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

via awk one liners

    
por 16.03.2011 / 21:56
5

Como você pediu para fazer isso no bash, aqui está uma solução que não faz uso do awk, sed ou perl, apenas uma função 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 instância da função chamada recursivamente. Então cuidado com arquivos grandes.

    
por 17.03.2011 / 09:46
4

Você pode canalizar:

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

O awk prefixa cada linha com o número da linha seguido por um espaço. O sort inverte a ordem das linhas ao classificar no primeiro campo (número da linha) na ordem inversa, estilo numérico. E o sed retira os números das linhas.

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'

Produz:

l
k
j
i
h
g
f
e
d
c
b
a
    
por 16.03.2011 / 11:57
1

Em perl:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'
    
por 16.03.2011 / 15:14
0

Solução somente BASH

leia o arquivo na matriz bash (uma linha = um elemento da matriz) e imprima a matriz 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
    
por 31.07.2015 / 13:40
0

Bash, com mapfile mencionado nos comentários para o 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 à solução sed e fica mais rápido à medida que o número de linhas solicitadas diminui.

    
por 18.09.2015 / 18:45
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'

    
por 12.10.2018 / 14:23
-3
awk '{print NR" "$0}' | sort -nr
    
por 11.04.2012 / 05:12