Gzippt eine große Menge symbolisch verknüpfter Dateien

Gzippt eine große Menge symbolisch verknüpfter Dateien

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 execAufruf gzipden Fortschritt behindert.

Wenn es sich um viele kleine Dateien handeln würde, gzipwä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, lsofeine gzipPID im Hintergrund anzuwenden und herauszufinden, was passiert.

verwandte Informationen