Сжатие большого количества файлов с символическими ссылками

Сжатие большого количества файлов с символическими ссылками

У меня есть папка, содержащая большое количество файлов с символическими ссылками. Каждый из этих файлов имеет размер порядка 10-11 ГБ (конкретно, файлы fastq). Они берутся из разных исходных папок, но я убедился, что есть только один уровень символических ссылок.

Я пытаюсь сжать их, просто выполнив:

gzip *.fastq

Это приводит к куче

too many levels of symbolic links

И, таким образом, терпит неудачу.

Однако, когда я это делаю:

for i in `ls | egrep *.fastq$`; do gzip -c $i > $i.gz; done;

это работает. Мой вопрос прост. В чем разница между ними? AFAIK, единственная разница в том, что второй подход запускает новый процесс gzip для каждого файла, тогда как первый должен делать все в одном процессе. Может ли gzip обрабатывать только один файл с символической ссылкой за раз? Выполнение того же самого в тестовой папке с обычными файлами работает в обоих направлениях.

решение1

Быстрая проверка исходного кода gzip (в частности, gzip 1.6, включенного в Ubuntu 14.04) показывает, что наблюдаемое поведение обусловлено функциейоткрыть_и_статистика, начиная со строки 1037 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;
}

Обратите внимание, что в строке комментария указано, что gzip не будет следовать по символическим ссылкам, если он не вызван с флагами -c или -f, а внутри #if ... #endif переменная errno установлена ​​в значение ELOOP (обнаружено слишком много символических ссылок), если файл, который нужно сжать, на самом деле является символической ссылкой.

Теперь, согласно странице руководства gzip(1), флаги -c и -f следующие:

   -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.

Собираем все вместе и возвращаемся к первоначальному вопросу:

  • Первый пример не работает, потому что он пытается сжать фактическую символическую ссылку (даже если онанетфактический цикл ссылок)
  • Во втором случае используется флаг -c, поэтому он считывает содержимое исходного файла, а затем записывает сжатый вывод в stdout, поэтому он выполняется успешно.
  • Третий сценарий — использование -f вместо -c. В этом случае gzip не жалуется при попытке сжать символическую ссылку, но при распаковке она становится обычным файлом, как показано:
$ ls -l
всего 4
-rw-rw-r-- 1 x86tux x86tux 13 июня 16 13:10 realfile.txt
lrwxrwxrwx 1 x86tux x86tux 12 июня 16 23:40 symlink.txt -> realfile.txt
$ gzip символическая ссылка.txt
gzip: symlink.txt: Слишком много уровней символических ссылок
$ gzip -f символическая ссылка.txt
$ ls -l
всего 8
-rw-rw-r-- 1 x86tux x86tux 13 июня 16 13:10 realfile.txt
-rw-rw-r-- 1 x86tux x86tux 45 июн 16 13:10 symlink.txt.gz
$ gunzip символическая ссылка.txt.gz
$ ls -l
всего 8
-rw-rw-r-- 1 x86tux x86tux 13 июня 16 13:10 realfile.txt
-rw-rw-r-- 1 x86tux x86tux 13 июня 16 13:10 symlink.txt
$ md5sum *
618f486e0225d305d16d0648ed44b1eb реальный файл.txt
618f486e0225d305d16d0648ed44b1eb символическая ссылка.txt

решение2

Отдельный процесс на часть файла может навредить, если есть вероятность, что он станет узким местом вашей работы, но при объеме в 10–11 гигабайт очень сложно представить себе сценарий, в котором именно вызов execбудет gzipтормозить прогресс.

В том же духе, если бы это были несколько небольших файлов, то, gzipскорее всего, сжать их тоже не удалось бы, поскольку для сравнения каждого файла требовалось бы меньше данных, но, опять же, при 10–11 гигабайтах на операцию сжатия это не будет проблемой.

Хотя, я думаю, было бы интересно выяснить причину ошибки. Я бы предложил попробовать обратиться lsofк фоновому gzippid и выяснить, что происходит.

Связанный контент