Najlepszy sposób na usunięcie bajtów z początku pliku?

61

Dzisiaj musiałem usunąć pierwsze 1131 bajtów z 800 MB mieszanego pliku tekstowego / binarnego, przefiltrowanego zrzutu subwersji, który włamuję do nowego repozytorium. Jak najlepiej to zrobić?

Na początek próbowałem

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

ale po pominięciu kopiuje pozostałą część pliku bajt na raz, tj. bardzo powoli. W końcu doszedłem do wniosku, że potrzebowałem 405 bajtów, aby zaokrąglić to do trzech bloków 512, które mogłem pominąć

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

które zakończyło się dość szybko, ale musiał istnieć prostszy / lepszy sposób? Czy jest inne narzędzie, o którym zapomniałem? Dzięki!

Rup
źródło
ddjest właściwym narzędziem do pracy - wygląda na to, że wymyśliłeś ładne, eleganckie rozwiązanie swojego problemu.
Justin Ethier,

Odpowiedzi:

62

Możesz przełączać opcje bs i pomijać:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

W ten sposób operacja może skorzystać z większego bloku.

W przeciwnym razie możesz spróbować z ogonem (chociaż nie jest bezpiecznie używać go z plikami binarnymi):

tail -c +1132 filtered.dump >trimmed.dump

Na koniec możesz użyć instancji 3 dd, aby napisać coś takiego:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

gdzie pierwsze dd wypisuje standardowe wyjście filter.dump; drugi odczytuje tylko 1131 bajtów i wyrzuca je; następnie ostatni odczytuje ze swojego standardowego wejścia pozostałe bajty filter.dump i zapisuje je do trimmed.dump.

marco
źródło
6
Dzięki! Nie wiedziałem, że dane przesyłane przez potok są przenoszone do drugiego takiego procesu - to bardzo miłe. Nie mogę uwierzyć, że nie myślałem o bs=1131 skip=1: - /
Rup
2
Większość współczesnych implementacji narzędzi powłoki działa poprawnie z plikami binarnymi (tzn. Nie mają problemów ze znakami zerowymi i nie wstawią dodatkowej nowej linii na końcu pliku). Z pewnością implementacje GNU i * BSD są bezpieczne.
Gilles
17

Nie jestem pewien, kiedy skip_byteszostał dodany, ale aby pominąć pierwsze 11 bajtów, masz:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

Gdzie iflag=skip_byteskaże dd interpretować wartość skipopcji jako bajty zamiast bloków, co czyni ją prostą.

Ярослав Рахматуллин
źródło
Z pewnością przewaga prędkości w przypadku dużych plików i niewielkiej ilości danych do usunięcia.
sstn
To najlepsza odpowiedź, ponieważ działa dla każdego rozmiaru bloku, np.iflag=skip_bytes skip=1234 bs=1M
phiresky
15

Możesz użyć podpowłoki i dwóch ddtakich wywołań:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig
maxschlepzig
źródło
1
Dzięki - nie wiedziałem, że dane wejściowe przesyłane przez potoki przechodzą do drugiego takiego procesu, chyba jest to podpowłoka? Na pewno to zapamiętam! Dałem Marco kleszczowi, ponieważ przybył tu pierwszy, ale +1 i dziękuję za odpowiedź!
Rup
1
@Rup, tak, podpowłoka - utworzona za pomocą nawiasów - zapewnia deskryptor pliku stdin i oba wywołania dd sukcesywnie wykorzystują dane wejściowe z niego. Tak - Marco pokonał mnie o 29 sekund :)
maxschlepzig
6

Jeśli system plików i jądro Linuksa go obsługują, możesz spróbować fallocatewprowadzić zmiany w miejscu: w najlepszym przypadku nie ma żadnych danych we / wy:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

gdzie <magic>zależy od systemu plików, wersji systemu Linux i typu pliku ( FALLOC_FL_COLLAPSE_RANGElub FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEmoże być używany wewnętrznie ).

jfs
źródło
1
To jest moja preferowana metoda, ale jej uruchomienie w kontenerze ma swoje problemy. stackoverflow.com/questions/31155591/…
michaelcurry
3

Powinieneś użyć count=0- to proste, lseek()gdy tylko jest to możliwe.

Lubię to:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddwykona lseek()deskryptor pliku wejściowego z przesunięciem 1131 bajtów, a następnie catpo prostu skopiuje wszystko, co pozostanie na wyjściu.

mikeserv
źródło
2

Jeszcze innym sposobem na usunięcie wiodących bajtów z pliku (bez użycia ddw ogóle) jest użycie xxdi sedlub tailodpowiednio.

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump
wop
źródło
To miłe, ale myślę, że wolę po prostu pracować z plikiem w formacie binarnym, niż konwertować go na format szesnastkowy iz niego.
Rup
2

@maxschlepzig prosi o liner online. Oto jeden w perlu. Wymaga 2 argumentów: od bajtu i długości. Plik wejściowy musi być podany jako „<”, a wyjście będzie ustawione na standardowe wyjście:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift;
     while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){
        $left -= $read; syswrite(STDOUT,$buf);
     }' 12345678901 19876543212 < bigfile > outfile

Jeśli długość jest większa niż plik, reszta pliku zostanie skopiowana.

W moim systemie zapewnia to 3,5 GB / s.

Ole Tange
źródło
Myślę, że jego jedno-liniowe wyzwanie polegało na tym, abyś udowodnił, że rozwiązanie w języku skryptowym było lepsze niż jego jednowierszowe rozwiązanie powłoki. I wolę jego: jest dla mnie krótszy i jaśniejszy. Jeśli twój działa lepiej, to dlatego, że używasz większego rozmiaru bloku niż on, co można łatwo zwiększyć w jego wersji.
Rup
@Rup Niestety, ale nie. Wydaje się, że zapominasz, że ddnie gwarantuje to pełnego odczytu. Spróbuj: tak | dd bs = 1024k liczba = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange
Również moje rozwiązanie nie odczytuje bajtów, których nie potrzebujesz (które mogą mieć długość kilku terabajtów).
Ole Tange