2009-08-13 13 views
11

Próbuję skopiować porcję z jednego pliku binarnego do nowego pliku. Mam przesunięcie bajtu i długość porcji, którą chcę pobrać.Jak pobrać dowolny fragment z pliku na systemie Unix/Linux

Próbowałem za pomocą programu narzędziowego dd, ale to wydaje się czytać i usunąć dane do offsetu, a nie tylko poszukiwanie (chyba dlatego, dd jest do kopiowania/konwertowania bloki danych). . To sprawia, że ​​dość wolno (i wolniej wyższa przesunięcie to jest polecenie próbowałem:

dd if=inputfile ibs=1 skip=$offset count=$datalength of=outputfile 

Chyba mogę napisać mały Perl/Python/cokolwiek skrypt do otwarcia pliku, dążyć do przesunięcia, potem czytać i pisać wymaganą ilość danych w kawałki.

Czy istnieje narzędzie, które obsługuje coś takiego?

+0

Próbowałem tylko działa na 'strace' dd, kiedyś llseek. – Hasturkun

+0

to byłby GNU dd, btw. – Hasturkun

+1

Ah, jestem na freebsd, więc może to inna implementacja. Może to jest powolne, ponieważ ustawiam rozmiar bufora wejściowego na 1 bajt. – kevinm

Odpowiedz

10

Tak, trudno dziś zrobić to z dd. Rozważamy dodanie paragrafów skip_bytes i count_bytes do dd w coreutils, aby pomóc. Poniższy powinien działać mimo:

#!/bin/sh 

bs=100000 
infile=$1 
skip=$2 
length=$3 

(
    dd bs=1 skip=$skip count=0 
    dd bs=$bs count=$(($length/$bs)) 
    dd bs=$(($length % $bs)) count=1 
) < "$infile" 
+0

Tak, dodanie skip/count_bytes byłoby naprawdę użyteczne i sprawiłoby, że dd byłby łatwym w użyciu buforem ogólnego przeznaczenia :) – kevinm

12

Możesz użyć tail -c+N, aby przyciąć początkowe bajty N z wejścia, następnie możesz użyć head -cM, aby wyprowadzić tylko pierwsze M bajtów ze swojego wejścia.

$ echo "hello world 1234567890" | tail -c+9 | head -c6 
rld 12 

więc stosując zmienne, to będzie prawdopodobnie:

tail -c+$offset inputfile | head -c$datalength > outputfile 


Ach, nie widzi on musiał szukać. Pozostawiając to jako CW.

1

Dzięki za inne odpowiedzi. Niestety, nie jestem w stanie zainstalować dodatkowego oprogramowania, więc opcja ddrescue jest niedostępna. Rozwiązanie "głowa/ogon" jest interesujące (nie zdawałem sobie sprawy, że możesz dostarczyć + do końca), ale skanowanie danych sprawia, że ​​jest dość powolny.

Skończyło się na tym, że napisałem mały skrypt Pythona, aby zrobić to, co chciałem. Rozmiar bufora prawdopodobnie powinien być ustawiony tak, aby był taki sam jak ustawienie zewnętrznego bufora, ale użycie poniższej wartości jest wystarczająco wydajne w moim systemie.

#!/usr/local/bin/python 

import sys 

BUFFER_SIZE = 100000 

# Read args 
if len(sys.argv) < 4: 
    print >> sys.stderr, "Usage: %s input_file start_pos length" % (sys.argv[0],) 
    sys.exit(1) 
input_filename = sys.argv[1] 
start_pos = int(sys.argv[2]) 
length = int(sys.argv[3]) 

# Open file and seek to start pos 
input = open(sys.argv[1]) 
input.seek(start_pos) 

# Read and write data in chunks 
while length > 0: 
    # Read data 
    buffer = input.read(min(BUFFER_SIZE, length)) 
    amount_read = len(buffer) 

    # Check for EOF 
    if not amount_read: 
     print >> sys.stderr, "Reached EOF, exiting..." 
     sys.exit(1) 

    # Write data 
    sys.stdout.write(buffer) 
    length -= amount_read 
+0

Rozmiar bufora powinien być wystarczająco duży, aby zmniejszyć liczbę kontrolerów systemowych (i przełączników kontekstu) oraz wielokrotność rozmiaru strony, aby buforowanie było jak najbardziej szczęśliwe. Kernel readahead oznacza, że ​​nie będzie to miało rzeczywistego wpływu na rozmiar żądanego we/wy dysku. 100000 nie jest wielokrotnością 4kiB, ale wartości od 64kiB do 1MiB są uzasadnione. – hobbs

1

Według mandd on FreeBSD:

skip=n

Przejdź n bloki od początku wejściowego przed kopiowaniem. Na wejściu obsługującym seki używana jest operacja lseek (2). W przeciwnym razie dane wejściowe są odczytywane i odrzucane. W przypadku rur odczytywana jest poprawna liczba bajtów . W przypadku wszystkich innych urządzeń poprawna liczba bloków jest odczytywana bez rozróżniania między częściowym lub całkowitym odczytanym blokiem .

Korzystanie dtruss I sprawdzeniu, że nie używa lseek() na pliku wejściowego na Mac OS X. Jeśli uważasz, że jest powolny, zgadzam się z komentarzem, że będzie to spowodowane 1-bajtowym rozmiarem bloku.

0

Można spróbować komendy HexDump.

hexdump -v <File Path> -c -n <No of bytes to read> -s <Start Offset> | awk '{$1=""; print $0}' | sed 's/ //g' 

Ex) Czytaj 100 bajtów z 'mycorefile' począwszy od offsetu 100.

# hexdump -v -c mycorefile -n 100 -s 100 | awk '{$1=""; print $0}' | sed 's/ //g' 
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 
\0\0\0\0001\0\0\0005\0\0\0\0020003\0 
\0\0\0\0\0\[email protected]\0\0\0\0\0\0\0\0\0 
\0\0\0\0\0\0\0\0\0\0\0\0\0 003\0 
\0\0\0\0\0020\0\0\0\0\0\0001\0\0\0 
006\0\0\0\0020003\0\0\0\0\0\0220c\0 
\0\0\0\0 

Następnie, używając innego skryptu, dołącz wszystkie linie wyjścia do pojedynczej linii, jeśli chcesz.

Jeśli po prostu chcesz zobaczyć zawartość:

# /usr/bin/hexdump -v -C mycorefile -n 100 -s 100 
00000064 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 
00000074 00 00 00 00 01 00 00 00 05 00 00 00 00 10 03 00 |................| 
00000084 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 |[email protected]| 
00000094 00 00 00 00 00 00 00 00 00 00 00 00 00 a0 03 00 |................| 
000000a4 00 00 00 00 00 10 00 00 00 00 00 00 01 00 00 00 |................| 
000000b4 06 00 00 00 00 10 03 00 00 00 00 00 00 90 63 00 |..............c.| 
000000c4 00 00 00 00          |....| 
000000c8 
# 
Powiązane problemy