Como obter comportamento inverso para 'tail' e 'head'?

50

Existe uma maneira de head / tail um documento e obter a saída reversa; porque você não sabe quantas linhas existem em um documento?

Ou seja. Eu só quero que tudo, exceto as duas primeiras linhas de foo.txt , sejam anexadas a outro documento.

    
por chrisjlee 16.08.2011 / 02:23

9 respostas

50

Você pode usar isso para remover as duas primeiras duas linhas:

tail -n +3 foo.txt

e isso para remover as últimas duas linhas:

head -n -2 foo.txt

(assumindo que o arquivo termina com \n para o último)

Assim como para o uso padrão de tail e head , essas operações não são destrutivas. Use >out.txt se você quiser redirecionar a saída para algum novo arquivo:

tail -n +3 foo.txt >out.txt

No caso de out.txt já existir, ele substituirá esse arquivo. Use >>out.txt em vez de >out.txt se preferir que a saída seja anexada a out.txt .

    
por 16.08.2011 / 02:39
9

Se você quiser todas, exceto as primeiras linhas N-1, chame tail com o número de linhas +N . (O número é o número da primeira linha que você deseja reter, começando em 1, ou seja, +1 significa início no início, +2 significa ignorar uma linha e assim por diante).

tail -n +3 foo.txt >>other-document

Não há uma maneira fácil e portátil de pular as últimas N linhas. O% GNUhead permite head -n +N como contrapartida de tail -n +N . Caso contrário, se você tiver tac (por exemplo, GNU ou Busybox), poderá combiná-lo com a cauda:

tac | tail -n +3 | tac

Portavelmente, você pode usar um filtro do awk (não testado):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Se você quiser remover as últimas linhas de um arquivo grande, você pode determinar o deslocamento de byte da peça para truncar e executar o truncamento com dd .

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

Você não pode truncar um arquivo no começo, mas se precisar remover as primeiras linhas de um arquivo enorme, você pode mova o conteúdo ao redor .

    
por 16.08.2011 / 02:42
3

Na página tail man (GNU tail , isto é):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Assim, o seguinte deve anexar todos, exceto as duas primeiras linhas de somefile.txt a anotherfile.txt :

tail --lines=+3 somefile.txt >> anotherfile.txt
    
por 16.08.2011 / 02:38
3

Para remover as primeiras n linhas, o GNU sed pode ser usado. Por exemplo, se n = 2

sed -n '1,2!p' input-file

O ! significa "excluir este intervalo". Como você pode imaginar, um resultado mais complicado pode ser obtido, por exemplo

sed -n '3,5p;7p'

que mostrará a linha 3,4,5,7. Mais poder vem do uso de expressões regulares em vez de endereços.

A limitação é que os números das linhas devem ser conhecidos antecipadamente.

    
por 16.08.2011 / 09:50
1

Você pode usar diff para comparar a saída de head / tail com o arquivo original e, em seguida, remover o que é o mesmo, portanto, obtendo o inverso.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
    
por 25.07.2013 / 21:40
1

Enquanto tail -n +4 para a saída do arquivo que começa na quarta linha (todas exceto as 3 primeiras linhas) é padrão e portátil, sua contraparte head ( head -n -3 , todas exceto as últimas 3 linhas) não é. / p>

Portavelmente, você faria:

sed '$d' | sed '$d' | sed '$d'

Ou:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(cuidado com isso em alguns sistemas em que sed tem um espaço padrão de tamanho limitado, que não é dimensionado para valores grandes de n ).

Ou:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
    
por 19.05.2015 / 18:42
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Se <infile for um arquivo regular, lseek() , então sim, por todos os meios, sinta-se à vontade. O acima é uma construção totalmente compatível com POSIX.

    
por 14.07.2015 / 02:36
0

Espero ter entendido claramente sua necessidade.

Você tem várias maneiras de concluir sua solicitação:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Onde / etc / passwd é o seu arquivo

A segunda solução pode ser útil se você tiver um arquivo enorme:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"

Espero que ajude

    
por 13.07.2015 / 18:09
0

Minha abordagem é semelhante a Gilles, mas em vez disso, apenas inverto o arquivo com cat e pipe com o comando head.

tac -r thefile.txt | cabeça thisfile.txt     (substitui arquivos)

    
por 03.09.2018 / 02:42