Ich habe eine 1 TB-Datei. Ich möchte von Byte 12345678901 zu Byte 19876543212 lesen und das auf Standardausgabe auf einer Maschine mit 100 MB RAM setzen.
Ich kann leicht ein Perl-Skript schreiben, das dies tut. Sysread liefert 700 MB / s (was in Ordnung ist), aber Syswrite liefert nur 30 MB / s. Ich möchte etwas effizienteres, vorzugsweise etwas, das auf jedem Unix-System installiert ist und das in der Größenordnung von 1 GB / s liefern kann.
Meine erste Idee ist:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Das ist aber nicht effizient.
Bearbeiten:
Ich habe keine Ahnung, wie ich Syswrite falsch gemessen habe. Dies liefert 3,5 GB / s:
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-12345678901)) < bigfile
und vermeidet den yes | dd bs=1024k count=10 | wc
Albtraum.
bs=1M iflag=skip_bytes,count_bytes
Antworten:
Dies ist aufgrund der geringen Blockgröße langsam. Unter Verwendung einer aktuellen GNU
dd
( coreutils v8.16 + ) können Sie am einfachsten die Optionenskip_bytes
und verwendencount_bytes
:Aktualisieren
fullblock
Option hinzugefügt, wie in der @ Gilles-Antwort angegeben . Zuerst dachte ich, dass dies impliziert sein könntecount_bytes
, aber das ist nicht der Fall.Die im Folgenden genannten Probleme stellen möglicherweise ein Problem dar. Wenn
dd
Lese- / Schreibaufrufe aus irgendeinem Grund unterbrochen werden, gehen Daten verloren. Dies ist in den meisten Fällen unwahrscheinlich (die Gewinnchancen sind etwas geringer, da wir aus einer Datei und nicht aus einer Pipe lesen).Das Verwenden von a
dd
ohne die Optionenskip_bytes
undcount_bytes
ist schwieriger:Sie können auch mit verschiedenen Blockgrößen experimentieren, aber die Gewinne werden nicht sehr dramatisch sein. Siehe - Gibt es eine Möglichkeit, den optimalen Wert für den Parameter bs für dd zu bestimmen?
quelle
bs
nicht ein Faktor vonskip
?skip
gibt eine Reihe von Blöcken, keine Bytes. Vielleicht sind Sie verwirrt , daskip_bytes
im ersten Beispiel Bedeutung verwendet wirdskip
ist es in Bytes?bs
heißt4,096
, was bedeutet , dass Sie nicht mehr genau überspringen , dass4,096
Bytesdd
wobei der erste und der letzte verwendetbs=1
werden, um die Daten zu kopieren, die bei einer Blockausrichtung nicht beginnen oder enden.bs=1
sagtdd
, dass jeweils ein Byte gelesen und geschrieben werden soll. Es gibt einen Overhead für jedenread
undwrite
Aufruf, was dies langsam macht. Verwenden Sie einen größeren Block, um eine angemessene Leistung zu erzielen.Wenn Sie eine ganze Datei zu kopieren, zumindest unter Linux, habe ich festgestellt , dass
cp
undcat
sind schneller alsdd
, auch wenn Sie eine große Blockgröße angeben.So kopieren Sie nur einen Teil einer Datei, können Sie Rohr
tail
inhead
. Dies erfordert GNU Coreutils oder eine andere Implementierung, diehead -c
eine bestimmte Anzahl von Bytes kopieren muss (tail -c
in POSIX, aberhead -c
nicht in POSIX ). Ein schneller Benchmark unter Linux zeigt, dass dies langsamer ist alsdd
, vermutlich wegen der Pipe.Das Problem dabei
dd
ist, dass es nicht zuverlässig ist: Es kann Teildaten kopieren . Soweit ich weiß,dd
sicher ist , wenn sie auf eine normale Datei Lesen und Schreiben - siehe Wenn zum Kopieren von Daten dd geeignet ist? (oder, wenn read () und write () partiell sind) - aber nur solange es nicht durch ein Signal unterbrochen wird . Mit GNU coreutils können Sie dasfullblock
Flag verwenden, dies ist jedoch nicht portierbar.Ein weiteres Problem
dd
ist, dass es schwierig sein kann, eine funktionierende Blockanzahl zu finden, da sowohl die Anzahl der übersprungenen Bytes als auch die Anzahl der übertragenen Bytes ein Vielfaches der Blockgröße sein müssen. Sie können mehrere Aufrufe verwenden, umdd
: einen zum Kopieren des ersten Teilblocks, einen zum Kopieren des Großteils der ausgerichteten Blöcke und einen zum Kopieren des letzten Teilblocks - siehe Graemes Antwort für ein Shell-Snippet. Vergessen Sie jedoch nicht, dass Sie beim Ausführen des Skriptsfullblock
beten müssen,dd
damit alle Daten kopiert werden , es sei denn, Sie verwenden die Flagge .dd
Gibt einen Status ungleich Null zurück, wenn eine Kopie partiell ist, sodass der Fehler leicht erkannt werden kann, es jedoch keine praktische Möglichkeit gibt, ihn zu reparieren.POSIX hat auf Shell-Ebene nichts Besseres zu bieten. Mein Rat wäre, ein kleines C-Spezialprogramm zu schreiben (je nachdem, was Sie genau implementieren, können Sie es
dd_done_right
odertail_head
oder aufrufenmini-busybox
).quelle
yes | dd bs=1024k count=10 | wc
Problem noch nie . Böse.Mit
dd
:Alternativ mit
losetup
:Und dann
dd
,cat
... das Loop - Gerät.quelle
So kannst du das machen:
Das ist alles, was wirklich notwendig ist - es erfordert nicht viel mehr. In erster Linie
dd count=0 skip=1 bs=$block_size1
wirdlseek()
über regelmäßige Dateieingabe praktisch augenblicklich. Es besteht keine Möglichkeit, dass Daten übersehen werden oder andere Unwahrheiten darüber gemeldet werden. Sie können einfach direkt zu Ihrer gewünschten Startposition suchen. Da der Dateideskriptor der Shell gehört und die Shell ihndd
lediglich erbt, wirkt er sich auf die Cursorposition aus, sodass Sie ihn schrittweise ausführen können. Es ist wirklich sehr einfach - und es gibt kein Standardwerkzeug, das besser für die Aufgabe geeignet ist alsdd
.Dafür wird eine Blockgröße von 64 KB verwendet, was häufig ideal ist. Entgegen der landläufigen Meinung beschleunigen größere Blockgrößen die
dd
Arbeit nicht . Andererseits sind winzige Puffer auch nicht gut.dd
muss seine Zeit in Systemaufrufen synchronisieren, damit es nicht warten muss, bis Daten in den Speicher kopiert und wieder ausgegeben werden, sondern auch, damit es nicht auf Systemaufrufe warten muss. Sie möchten also, dass es genügend Zeit in Anspruchread()
nimmt , damit der nächste nicht auf den letzten warten muss, sondern nicht so viel, dass Sie in größeren Formaten als erforderlich puffern.Der Erste
dd
springt also zur Startposition. Das braucht keine Zeit. Sie können jedes andere Programm aufrufen, das Sie an diesem Punkt mochten, um seine Standard-ID zu lesen, und es würde direkt an Ihrem gewünschten Byte-Offset mit dem Lesen beginnen. Ich rufe einen anderendd
an, um((interval / blocksize) -1)
count blocks to stdout zu lesen .Das letzte, was notwendig ist, ist das Herauskopieren des Moduls (falls vorhanden) der vorherigen Divisionsoperation. Und das ist das.
Glauben Sie es übrigens nicht, wenn die Leute Tatsachen ohne Beweise auf ihrem Gesicht ausdrücken. Ja, es ist möglich
dd
, einen kurzen Lesevorgang durchzuführen (obwohl dies beim Lesen von einem fehlerfreien Blockgerät nicht möglich ist - daher der Name) . Solche Dinge sind nur möglich, wenn Sie einendd
Stream, der nicht von einem Block-Gerät gelesen wird, nicht korrekt puffern . Beispielsweise:In beiden Fällen werden alle Daten
dd
kopiert . Im ersten Fall ist es möglich (wenn auch unwahrscheinlich ) , dass einige der Ausgangsblöcke , die Kopien aus Willen gleich „$ num“ Bit - Bytes , weil geskilled wird nur überhaupt etwas zu puffern , wenn der Puffer speziell auf ihre Kommando- angefordert wird Linie. stellt eine maximale Blockgröße dar, da der Zweck von Echtzeit-E / A ist.cat
dd
dd
bs=
dd
Im zweiten Beispiel gebe ich explizit die Ausgabeblockgröße und die Pufferlesungen an,
dd
bis vollständige Schreibvorgänge durchgeführt werden können. Das wirkt sich nicht darauf aus,count=
welche auf Eingabeblöcken basieren, aber dafür brauchen Sie einfach einen anderendd
. Jegliche Fehlinformationen, die Sie ansonsten erhalten, sollten ignoriert werden.quelle