Unterscheiden einer regulären Datei von einem Symlink

22

Ich schreibe ein Bash-Skript, das eine reguläre Datei von einem Symlink unterscheiden muss. Ich dachte, ich könnte das mit if / test machen, aber es funktioniert nicht so, wie ich es erwartet hatte:

$ 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

Warum das? Und wie kann ich das richtig machen?

Nupraptor
quelle

Antworten:

20

Es sieht so aus, als würdest du deine Tests ein bisschen durcheinander bringen. Sie müssen nicht beide Tests ausführen. In diesem Fall müssen Sie nur -hfeststellen, ob es sich bei der Datei um einen Symlink handelt.

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

Der -fTest sagt Ihnen nur, ob das Objekt eine Datei ist. Dies würde zurückgeben, 0wenn es sich um ein Verzeichnis oder einen Geräteknoten oder einen Symlink zu einem Verzeichnis handelte, wird jedoch 1über einen Symlink zu einer Datei zurückgegeben.

Wenn Sie auch wissen möchten, ob es sich um einen Symlink zu einer Datei und nicht um ein Verzeichnis handelt, müssen Sie die Ergebnisse beider Tests mit ein wenig Logik kombinieren.

Caleb
quelle
Wie ich aus der Dokumentation verstanden, der Unterschied zwischen -eund -fwurde das -everwendet wurde , um zu wissen , ob eine Datei (jeglicher Art) bestand, und -fwar speziell zu testen , ob die Datei eine reguläre Datei existiert und war. Es scheint, ich habe falsch verstanden, was eine "normale Datei" war ..
Nupraptor
1
@Nupraptor: Ja, du hast die Dokumente falsch verstanden. Ein Symlink wird als reguläre Datei betrachtet, im Gegensatz zu einigen anderen Knotentypen, die eine Datei sein könnte (Blockgeräteknoten, Zeichengeräteknoten, Verzeichnis usw.). Wenn Sie wissen möchten, um welchen Dateityp es sich handelt, müssen Sie einen -hdateitypspezifischen Test durchführen, z. B. für Symlinks, Named -pPipes usw.
Caleb,
Wie teste ich dann, ob eine Datei eine reguläre Datei in dem Sinne ist, dass es sich nicht um eine Pipe oder einen Symlink usw. handelt? Soll ich dafür eine andere Frage stellen?
Nupraptor
@Nupraptor: Der einzige seltsame Fall ist ein Symlink, der auf eine reguläre Datei verweist. Andernfalls handelt es sich beim Testen als reguläre Datei um eine reguläre Datei.
David Schwartz
3
Das Verzeichnis test -f gibt eine 1 und keine 0 zurück: test -f. ; echo $? (Ausgänge 1)
Polynom
7

Bei @Caleb ist es richtig, das Skript nur auf den Symlink testen zu lassen. Der Teil über das Warum wurde jedoch ausgelassen und ich war neugierig. Wenn Sie sich den Quellcode von coreutils ansehen und die Ausgabe von test verfolgen, können Sie feststellen, dass beim Ausführen des Symbolic Link-Tests lstat verwendet wird und wenn Sie den -f-Test verwenden, wird tatsächlich 'stat' aufgerufen, der auf den Symlink folgt:

$ 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

Von der Stat Manpage:

   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.

Dies bedeutet, dass der Test -f true zurückgibt, solange der angegebene Dateiname ein Symlink zu einer regulären Datei oder einer regulären Datei selbst ist.

Polynom
quelle