У меня есть папка, содержащая большое количество файлов с символическими ссылками. Каждый из этих файлов имеет размер порядка 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
к фоновому gzip
pid и выяснить, что происходит.