Mit welchem ​​idempotenten Befehl kann ich einen Symlink erstellen, der auf ein Verzeichnis verweist?

16

Ich möchte einen Befehl in ein Shell-Skript einfügen, das einen Symlink zu einem Verzeichnis erstellt, aber dieses Skript könnte immer wieder ausgeführt werden, sodass der Befehl bei nachfolgenden Aufrufen nichts ändern sollte.

Hier ist die Verzeichnisstruktur:

% tree /tmp/test_symlink 
/tmp/test_symlink
├── foo
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

Ich möchte einen Symlink in foo/sogenannten Snippets erstellen, der auf das Verzeichnis verweist /tmp/test_symlink/repo/resources/snippets.
Also laufe ich:

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

was das gewünschte Ergebnis ergibt.

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

5 Verzeichnisse, 5 Dateien

Wenn der Befehl jedoch erneut ausgeführt wird,

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

Es wird ein Symlink zu einem Verzeichnis erstellt, in dem der Symlink bereits vorhanden ist. Symlink wird in das reale Verzeichnis eingefügt

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets -> /tmp/test_symlink/repo/resources/snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

Warum passiert das und wie kann ich den Befehl so ändern, dass nachfolgende Aufrufe diesen seltsamen Effekt nicht erzeugen?

the_velour_fog
quelle

Antworten:

16

Verwenden Sie dazu die -TOption, die angibt, den Linknamen lnimmer als den gewünschten Linknamen zu behandeln, niemals als Verzeichnis.

Wenn Sie ohne diese Option lneinen Linknamen angeben, der als Verzeichnis existiert, wird ein neuer Link zum Ziel in diesem Verzeichnis erstellt.

Beachten Sie, dass -Tes sich um einen GNU-Ismus handelt (zumindest nicht in POSIX), aber Sie verwenden bereits -veinen GNU-Ismus, also stelle ich mir vor, dass dies kein Problem ist.

Alternativ können Sie einfach das übergeordnete Verzeichnis als Linknamen angeben und der Link wird dort immer (neu) erstellt:

ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/

Dies funktioniert, weil Ihr Symlink denselben Namen wie das Ziel hat.

Stephen Kitt
quelle
danke, ja die gnu optionen sind nicht unbedingt portabel, aber sie können sehr nützlich sein - wenn portabilität keine voraussetzung ist. Ihre Antwort funktioniert für mich. Von der Manpage -n, --no-dereference treat LINK_NAME as a normal file if it is a symbolic link to a directory. -T, --no-target-directory treat LINK_NAME as a normal file alwayshalten Sie es für besser, einen Symlink immer als Datei zu behandeln? Ich hätte gedacht, es wäre besser, die Verwendung dieser "speziellen" Optionen zu begrenzen?
the_velour_fog
Wenn Sie wissen, dass sie verfügbar sind, gibt es keinen Grund, GNU-spezifische Optionen (IMO) zu vermeiden. Sie haben nach einem idempotenten Befehl gefragt, -Twie Sie lnidempotent machen . Es gibt andere Möglichkeiten, das gleiche Ergebnis zu erzielen, wenn Sie es vorziehen, z. B. den Link zu löschen und neu zu erstellen, oder wenn Sie bereits wissen foo, ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/dass dies eine bessere Antwort ist (ich werde es aktualisieren).
Stephen Kitt
1
cool, ja ich hatte vorher schon mit ifAussagen über die Gegenwart des Symlink zu erkennen und den Befehl überspringen , wenn der Symlink existierte, aber die Skripte am Ende überladen und immer schwer zu lesen, war ich für etwas Idempotent aber einfach gehen, wie mit mkdir -pkeine Tests , keine Logik, nur gute
alte
@ Stephen Kitt: Sie haben die Verwendung von -T erwähnt, aber ich konnte es nicht im Code-Snippet Ihrer Antwort finden. Missverstehe ich etwas oder vermisse ich etwas?
Guizmoo03
@ Guizmoo03 vielleicht hast du die "Alternative" verpasst, die das Snippet einführt. Die ersten drei Absätze erklären -T, aber das Ende meiner Antwort stellt eine andere Lösung dar, die nicht verwendet wird -T.
Stephen Kitt
-1

Das Beste, was ich hier feststellen kann, ist, dass sich der lnBefehl beim zweiten Durchlauf wie cpoder verhält. mvWenn er ein "Verzeichnis" unter <Ziel> sieht, wird die Datei darin abgelegt, andernfalls wird <Ziel> nicht abgelegt existiert, wird es <destination> machen. (Das ist es, was der "Slashdot" -Trick vermeidet - dh er stellt sicher, dass das <Ziel> existiert und ein Verzeichnis ist).
Aber ich könnte mich irren.

In beiden Fällen scheint dies zu funktionieren

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
the_velour_fog
quelle