Ich habe einen Ordner mit einer großen Anzahl symbolisch verknüpfter Dateien. Diese Dateien sind jeweils etwa 10-11 GB groß (genauer gesagt Fastq-Dateien). Sie stammen aus verschiedenen Quellordnern, aber ich habe sichergestellt, dass es nur eine Ebene symbolischer Links gibt.
Ich versuche, sie mit gzip zu komprimieren, indem ich einfach Folgendes mache:
gzip *.fastq
Das Ergebnis ist eine Menge
too many levels of symbolic links
Und scheitert damit.
Wenn ich das jedoch tue:
for i in `ls | egrep *.fastq$`; do gzip -c $i > $i.gz; done;
es funktioniert. Meine Frage ist einfach. Was ist der Unterschied zwischen diesen beiden? Soweit ich weiß, besteht der einzige Unterschied darin, dass der zweite Ansatz für jede Datei einen neuen Gzip-Prozess startet, während der erste alles in einem Prozess erledigen sollte. Kann Gzip nur eine symbolisch verknüpfte Datei gleichzeitig verarbeiten? Wenn man dasselbe in einem Testordner mit normalen Dateien macht, funktioniert es in beide Richtungen.
Antwort1
Eine schnelle Überprüfung der gzip-Quelle (insbesondere gzip 1.6, wie es in Ubuntu 14.04 enthalten ist) zeigt, dass das beobachtete Verhalten von der Funktion herrührtöffnen_und_stat, beginnend bei Zeile 1037 von gzip.c:
static int
open_and_stat (char *name, int flags, mode_t mode, struct stat *st)
{
int fd;
/* Refuse to follow symbolic links unless -c or -f. */
if (!to_stdout && !force)
{
if (HAVE_WORKING_O_NOFOLLOW)
flags |= O_NOFOLLOW;
else
{
#if HAVE_LSTAT || defined lstat
if (lstat (name, st) != 0)
return -1;
else if (S_ISLNK (st->st_mode))
{
errno = ELOOP;
return -1;
}
#endif
}
}
fd = OPEN (name, flags, mode);
if (0 <= fd && fstat (fd, st) != 0)
{
int e = errno;
close (fd);
errno = e;
return -1;
}
return fd;
}
Beachten Sie, dass in der Kommentarzeile angegeben wird, dass gzip keinen symbolischen Links folgt, es sei denn, es wird mit den Flags -c oder -f aufgerufen. Innerhalb von #if ... #endif wird die Variable errno auf ELOOP (zu viele symbolische Links gefunden) gesetzt, wenn es sich bei der zu komprimierenden Datei tatsächlich um einen symbolischen Link handelt.
Aus der Manpage gzip(1) können Sie entnehmen, dass die Flags -c und -f wie folgt lauten:
-c --stdout --to-stdout Write output on standard output; keep original files unchanged. If there are several input files, the output consists of a sequence of independently com‐ pressed members. To obtain better compression, concatenate all input files before compressing them. -f --force Force compression or decompression even if the file has multiple links or the corresponding file already exists, or if the compressed data is read from or written to a terminal. If the input data is not in a format recognized by gzip, and if the option --stdout is also given, copy the input data without change to the standard output: let zcat behave as cat. If -f is not given, and when not running in the background, gzip prompts to verify whether an existing file should be overwritten.
Alles in allem kommen wir zurück zur ursprünglichen Frage:
- Das erste Beispiel schlägt fehl, weil es versucht, den eigentlichen Symlink zu komprimieren (auch wenn ernichteine tatsächliche Verbindungsschleife)
- Die zweite Methode verwendet das Flag -c, liest also den Inhalt der Originaldatei und schreibt die komprimierte Ausgabe anschließend in die Standardausgabe, sodass die Methode erfolgreich ist.
- Ein drittes Szenario ist die Verwendung von -f statt -c. In diesem Fall beschwert sich gzip nicht, wenn versucht wird, einen symbolischen Link zu komprimieren, aber bei der Dekomprimierung wird daraus eine normale Datei, wie gezeigt:
$ ls -l insgesamt 4 -rw-rw-r-- 1 x86tux x86tux 13. Juni 16 13:10 realfile.txt lrwxrwxrwx 1 x86tux x86tux 12. Juni 16 23:40 symlink.txt -> realfile.txt $ gzip symlink.txt gzip: symlink.txt: Zu viele Ebenen symbolischer Links $ gzip -f symlink.txt $ ls -l insgesamt 8 -rw-rw-r-- 1 x86tux x86tux 13. Juni 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 45 Juni 16 13:10 symlink.txt.gz $ gunzip symlink.txt.gz $ ls -l insgesamt 8 -rw-rw-r-- 1 x86tux x86tux 13. Juni 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 13. Juni 16 13:10 symlink.txt $ md5sum * 618f486e0225d305d16d0648ed44b1eb realfile.txt 618f486e0225d305d16d0648ed44b1eb symlink.txt
Antwort2
Der Teil mit dem einzelnen Prozess pro Datei könnte für manche schädlich sein, wenn die Möglichkeit besteht, dass er Ihren Vorgang verlangsamt, aber bei 10 bis 11 Gigabyte ist es sehr schwer, sich ein Szenario vorzustellen, in dem der exec
Aufruf gzip
den Fortschritt behindert.
Wenn es sich um viele kleine Dateien handeln würde, gzip
wäre eine Komprimierung wahrscheinlich nicht so gut möglich, da pro Datei weniger Daten zum Vergleichen vorhanden wären. Bei 10 bis 11 Gigabyte pro Komprimierungsvorgang dürfte das jedoch kein Problem sein.
Ich denke allerdings, dass es interessant wäre, die Ursache des Fehlers herauszufinden. Ich würde vorschlagen, zu versuchen, lsof
eine gzip
PID im Hintergrund anzuwenden und herauszufinden, was passiert.