一般ファイルとシンボリックリンクの区別

一般ファイルとシンボリックリンクの区別

私はbashスクリプトを書いていますが、通常のファイルとシンボリックリンクを区別する必要があります。 if / test式を使用してこれを行うことができると思いましたが、期待どおりに機能しません。

$ touch regular_file
$ test -f regular_file; echo $?
0
$ test -h regular_file; echo $?
1
$ ln -s regular_file symlink
$ test -h symlink; echo $?
0
$ test -f symlink; echo $?
0

なぜそんなことですか?そしてどうすれば正しくできますか?

答え1

テストを少し台無しにしたようです。両方のテストのいずれかを実行する必要はありません。この場合、必要な唯一のテストは、ファイルが-hシンボリックリンクかどうかを示すテストです。

test -h file && echo "is symlink" || echo "is regular file"

使用中のテストは、-fオブジェクトがファイルであるか、ファイルへのリンクですが通知します。1ディレクトリ、デバイスノード、ディレクトリへのシンボリックリンクなどの場合は(失敗)、0ファイルへのシンボリックリンクの場合は(成功)を返します。つまり、一部のシンボリックリンクは真と評価され、他のシンボリックリンクは偽と評価されます。

ディレクトリ以外のファイルへのシンボリックリンクであるかどうかを知る必要がある場合は、両方のテストの結果を少し論理的に組み合わせる必要があります。

答え2

@Calebは、スクリプトがシンボリックリンクのみをテストできるようにするのが正しいです。ところで、なぜ抜けたのかという部分が気になりますね。 coreutilsソースコードを見てテスト結果に従うと、シンボリックリンクテストを実行するときにlstatを使用し、-fでテストすると実際にシンボリックリンクの後ろから「stat」を呼び出すことがわかります。

$ ln -s varnish_config XXX
$ strace -s 2000 test -L XXX 2>&1 | grep XXX
execve("/usr/bin/test", ["test", "-L", "XXX"], [/* 47 vars */]) = 0
lstat("XXX", {st_mode=S_IFLNK|0777, st_size=14, ...}) = 0

$ strace -s 2000 test -L varnish_config 2>&1 | grep varnish
execve("/usr/bin/test", ["test", "-L", "varnish_config"], [/* 47 vars */]) = 0
lstat("varnish_config", {st_mode=S_IFREG|0664, st_size=1046, ...}) = 0

$ strace -s 2000 test -f XXX 2>&1 | grep XXX
execve("/usr/bin/test", ["test", "-f", "XXX"], [/* 47 vars */]) = 0
stat("XXX", {st_mode=S_IFREG|0664, st_size=1046, ...}) = 0

統計マニュアルページから:

   stat() stats the file pointed to by path and fills in buf.

   lstat() is identical to stat(), except that if path is a symbolic link,
   then the link itself is stat-ed, not the file that it refers to.

これは、指定されたファイル名が通常のファイルであるか、通常のファイル自体へのシンボリックリンクの場合は、-fテストがtrueを返すことを意味します。

答え3

ほとんどのtestテストファイル関連演算子はこれを行います。後ろにもしそうなら、シンボリックリンクチェックtest -f symlinkはtrueを返します。symlink定期的なtest -s symlinkシンボリックリンク分析後に決定されたファイルは、空でないファイル、test -d symlinkディレクトリファイル、またはtest -r symlink読み取り可能ファイルと同じです。

stat()これにはシステムコールを使用します。

唯一の例外は-L(通常はより好ましい-h)です。ファイルがシンボリックリンクであるかどうかをテストするには、lstat()最終的に確認されるファイルではなく、リンク自体に関する情報を取得するためにaを実行する必要があります。

だからファイルがあるかどうかを確認するには定期的な文書今後シンボリックリンクを解決するには、次のものが必要です。

if test -f "$file" && test ! -L "$file"; then
  echo is a regular file
fi

シンボリックリンク、一般タイプ、その他のタイプを明確に区別するには、まず次の作業を実行するtest -L必要があります。

if test -L "$file"; then
  echo symlink
elif test -f "$file"; then
  echo regular # strictly speaking, it could also have been replaced
               # with a symlink to a regular file since the previous test
else
  echo other type or not accessible
fi

関連情報