Truncar arquivo em um pipe

3

Existe uma maneira simples de truncar um arquivo em um pipe? Especificamente, quero cortar os últimos quatro bytes de um arquivo antes de alimentá-lo em outro processo.

Idealmente, eu seria capaz de escrever algo como:

cat input.txt | some-process | truncate --size=-4 | another-process > output.txt

mas parece que o comando truncate só funciona "no lugar" em um arquivo no disco.

    
por kostmo 24.02.2012 / 05:29

6 respostas

3

Eu me sinto bobo depois de escrever esse script Python.

Existe um comando de shell interno head para fazer isso:

cat input.txt | some-process | head --bytes=-4 | another-process > output.txt

Editar: O comando head do GNU tem um implementação conceitualmente similar (ou seja, uso eficiente de memória) para minha implementação em Python abaixo. Uma diferença é que ele arredonda o tamanho do buffer circular ( N , o número de bytes omitidos) para um múltiplo de algum tamanho de buffer padrão.

    
por 24.02.2012 / 11:05
4

Isto é como se eu lhe dissesse para levantar a mão assim que eu pronunciasse o quarto da última palavra que estou prestes a dizer. Eu não vou lhe dizer de antemão quantas palavras estou prestes a dizer.

Um canal é um fluxo . Seus dados não têm tamanho, ele só tem operações para obter o próximo elemento e / ou inserir um elemento nele, e o resultado é um dado ou um sinal de que não há mais dados.

Assim, a menos que você primeiro recupere todos os dados do fluxo, coloque-os em um buffer, conte seu comprimento, "retroceda" o fluxo e recupere quatro elementos a menos, isso não pode ser feito.

EDIT: Eu preciso fazer mais coisas pensando em vez de chegar com analogias inteligentes :) Um fluxo não diz "me pare imediatamente n elementos antes do último", mas sim "transmitir todos os elementos, exceto o último n", e mantendo um buffer de apenas n elementos, e esperando até que os primeiros n elementos tenham sido recebidos antes de transmitir o primeiro, é possível. Obviamente, isso não funcionará em situações como telecomunicações, onde você deseja que os dados sejam enviados imediatamente após serem recebidos, como você poderia, se quisesse os primeiros n elementos. E eu suponho que truncate não faz desse jeito.

(tentativa de downvoting self -1)

    
por 24.02.2012 / 06:48
1

sed pode operar na última linha. Isso pressupõe que os últimos 4 caracteres estão em uma única linha:

printf "%s\n" abcdef ghijkl mnopqr | sed '$s/....$//'

saídas

abcdef
ghijkl
mn
    
por 24.02.2012 / 16:05
0

Não consegui encontrar comandos de shell internos para fazer isso, então acho que isso significa que não há solução de "uma linha". No entanto, consegui escrever um script em Python para fazer o que preciso:

#!/usr/bin/env python
'''
Usage:
pipetruncate.py <N>

Truncates a stream in a pipe at N bytes before the EOF.
Uses memory proportional to N.
'''

import sys

buffer_length = int(sys.argv[1])
circular_buffer = [0]*buffer_length
count = 0
while True:
    ch = sys.stdin.read(1)
    if not len(ch): # EOF
        break

    index = count % buffer_length
    nextchar = circular_buffer[index]
    circular_buffer[index] = ch

    count += 1
    if count > buffer_length:
        sys.stdout.write(nextchar)

sys.stdout.close()

Então eu invoco

cat input.txt | some-process | ./pipetruncate.py 4 | another-process > output.txt

    
por 24.02.2012 / 10:53
0

Passou parte da manhã escrevendo um script python também. Claro, é melhor você usar sua "cabeça" em vez de escrever mais código. De qualquer forma aqui é a minha versão. É feio, mas acho que é meu primeiro script python:

#!/usr/bin/python

# stream_trunc: cut the last n bits of a stream

import sys

if len(sys.argv) <> 2:
    print 'Usage: ' + sys.argv[0] + ' <number>'
    exit(1)

num = sys.argv[1]

if num.isdigit() != True:
    print 'Argument should be a number'
    print 'Usage: ' + sys.argv[0] + ' <number>'
    exit(1)

n = int(num)
buf = sys.stdin.read(n)
c = sys.stdin.read(1)

while c != '':
    sys.stdout.write(buf[0])
    buf = buf[1:] + c
    c = sys.stdin.read(1)
    
por 24.02.2012 / 12:31
0

Estou surpreso que ninguém tenha mencionado dd ainda.

Isto irá ler os primeiros 1024 bytes de entrada:

$ dd if=inputfile of=truncated_file count=1024

Isso irá ignorar os primeiros 2048 bytes de entrada:

$ dd if=inputfile of=truncated_file skip=2048

Ao remover os parâmetros if e / ou of , dd lerá de STDIN e gravará em STDOUT. Isso significa que você pode fazer coisas assim:

$ cat input.txt | dd count=1024 | another-process > output.txt

Dependendo de qual versão de dd você está executando, é possível especificar unidades de tamanho para os parâmetros count e skip (consulte a man page para obter mais detalhes).

    
por 25.07.2017 / 23:51