Усеченный файл в трубе

3

Есть ли простой способ обрезать файл в трубе? В частности, я хочу обрезать последние четыре байта файла перед передачей его в другой процесс.

В идеале я мог бы написать что-то вроде:

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

но похоже, что truncateкоманда действует только «на месте» над файлом на диске.

kostmo
источник
1
cat input.txt | some-processлучше написано как some_process < input.txt.
Бенуа

Ответы:

5

Это как если бы я сказал вам поднять руку, как только произнесу четвертое от последнего слова, которое я собираюсь сказать. Я не собираюсь говорить вам заранее, сколько слов я собираюсь сказать.

Труба - это поток . Его данные не имеют размера, у него есть только операции для получения следующего элемента из него и / или вставки в него элемента, и результатом является либо фрагмент данных, либо сигнал о том, что данных больше нет.

Таким образом, если вы сначала не извлечете все данные из потока, не поместите их в буфер, не подсчитаете их длину, не перемотаете поток и не получите на четыре элемента меньше, это невозможно сделать.

РЕДАКТИРОВАТЬ: мне нужно сделать больше продумывая вещи вместо того, чтобы придумывать умные аналогии :) Поток не говорит «немедленно остановить меня n элементов перед последним», а скорее «передать все элементы, кроме последнего n», и путем поддержания буфер из только n элементов, и ожидание, пока первые n элементов не будут получены, прежде чем передать первый, возможно. Очевидно, что это не будет работать в таких ситуациях, как телекоммуникации, когда вы хотите, чтобы данные отправлялись сразу после их получения, как вы могли бы, если бы вам нужны были первые n элементов. И я полагаю truncate, не делает это таким образом.

(попытка понизить самость -1)

Пол Рихтер
источник
У вас есть мое одобрение, потому что ваш ответ помог мне понять, что длина файла / потока неизвестна во время выполнения, что побудило меня написать скрипт на Python.
Костмо
4

Я чувствую себя глупо после написания этого скрипта Python.

Для этого есть встроенная команда оболочки head:

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

Редактировать: Команда GNU headимеет концептуально похожую реализацию (т.е. эффективную для памяти) с моей реализацией Python ниже. Одно из отличий состоит в том, что он округляет размер кольцевого буфера ( Nколичество пропущенных байтов) до кратного некоторого стандартного размера буфера.

kostmo
источник
предоставленная headверсия не слишком старая, которая будет работать.
Бенуа
1

sedможет работать на последней строке. Это предполагает, что последние 4 символа находятся в одной строке:

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

выходы

abcdef
ghijkl
mn
Гленн Джекман
источник
0

Я не смог найти никаких встроенных команд оболочки, чтобы сделать это, поэтому я предполагаю, что это означает, что не существует «однострочного» решения. Тем не менее, я смог написать скрипт Python, чтобы сделать то, что мне нужно:

#!/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()

Тогда я призываю

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

kostmo
источник
0

Потратил часть утра на написание скрипта на python. Конечно, вам лучше использовать свою «голову» вместо того, чтобы писать больше кода. В любом случае, вот моя версия. Это ужасно, но я думаю, что это мой первый скрипт на 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)
Хорхе Хуан
источник
0

Я удивлен, что никто ddеще не упоминается .

Это будет читать первые 1024 байта ввода:

$ dd if=inputfile of=truncated_file count=1024

Это пропустит первые 2048 байтов ввода:

$ dd if=inputfile of=truncated_file skip=2048

Удаляя параметр ifи / или ofпараметр (ы), ddбудет считывать из STDIN и записывать в STDOUT. Это означает, что вы можете делать такие вещи:

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

В зависимости от того, какой версии ddвы работаете, вы можете указать единицы размера для countи skipпараметров (см страницы человека для более подробной информации).

КЧР
источник