大量のシンボリックリンク ファイルを含むフォルダーがあります。これらのファイルはそれぞれ 10 ~ 11 GB 程度です (具体的には fastq ファイル)。これらはさまざまなソース フォルダーから取得されますが、シンボリックリンクのレベルは 1 つだけであることを確認しました。
私は単純に次のようにしてそれらを gzip 圧縮しようとしています:
gzip *.fastq
その結果、
too many levels of symbolic links
そして失敗します。
しかし、私がそうすると:
for i in `ls | egrep *.fastq$`; do gzip -c $i > $i.gz; done;
確かに動作します。私の質問は簡単です。これらの違いは何ですか? 私の知る限り、唯一の違いは、2 番目のアプローチではファイルごとに新しい gzip プロセスを開始するのに対し、最初のアプローチではすべてを 1 つのプロセスで実行することです。gzip は一度に 1 つのシンボリックリンク ファイルしか処理できませんか? 通常のファイルを含むテスト フォルダーで同じことを行うと、どちらの方法でも動作します。
答え1
gzipソース(具体的にはUbuntu 14.04に含まれるgzip 1.6)を簡単に確認すると、観察された動作は次の関数から来ていることがわかります。オープンと統計gzip.c の 1037 行目から始まります:
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.
すべてをまとめると、元の質問に戻ります。
- 最初の例は、実際のシンボリックリンクを圧縮しようとしているため失敗します(ない実際のリンクループ)
- 2 番目は -c フラグを使用しているため、元のファイルの内容を読み取り、圧縮された出力を stdout に書き込むため、成功します。
- 3 番目のシナリオは、-c の代わりに -f を使用することです。この場合、gzip はシンボリック リンクを圧縮しようとしてもエラーを出力しませんが、解凍すると次のように通常のファイルになります。
$ ls -l 合計 4 -rw-rw-r-- 1 x86tux x86tux 13 6月 16 13:10 realfile.txt lrwxrwxrwx 1 x86tux x86tux 12 Jun 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 6月 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 45 6月16日 13:10 symlink.txt.gz $ gunzip シンボリックリンク.txt.gz $ ls -l 合計 8 -rw-rw-r-- 1 x86tux x86tux 13 6月 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 13 6月 16 13:10 symlink.txt $ md5sum * 618f486e0225d305d16d0648ed44b1eb 実ファイル.txt 618f486e0225d305d16d0648ed44b1eb シンボリックリンク.txt
答え2
ファイルごとに 1 つのプロセスを実行する部分は、操作のボトルネックになる可能性がある場合は多少問題になるかもしれませんが、10 ~ 11 ギガバイトの場合、呼び出しによってexec
進行gzip
が妨げられるシナリオを想像するのは非常に困難です。
同様に、小さなファイルが多数ある場合は、gzip
ファイルごとに比較するデータが少なくなるため、圧縮できない可能性が高くなりますが、この場合も、圧縮操作ごとに 10 ~ 11 ギガバイトなので、問題にはなりません。
ただし、エラーの原因を発見するのは興味深いと思います。lsof
バックグラウンドのgzip
pid に適用して、何が起こっているのか調べることをお勧めします。