Quelle commande idempotent puis-je utiliser pour créer un lien symbolique pointant vers un répertoire?

16

Je veux mettre une commande dans un script shell qui créera un lien symbolique vers le répertoire, mais ce script pourrait être exécuté encore et encore, donc lors des appels ultérieurs, la commande ne devrait rien changer.

Voici la structure du répertoire:

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

Je veux créer un lien symbolique dans des foo/extraits appelés qui pointe vers le répertoire /tmp/test_symlink/repo/resources/snippets.
Je lance donc:

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

ce qui donne le résultat souhaité.

% 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 répertoires, 5 fichiers

Cependant, lorsque la commande est réexécutée,

% 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'

il crée un lien symbolique vers un répertoire, où le lien symbolique existe déjà met le lien symbolique à l'intérieur du répertoire réel

% 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

pourquoi cela se produit-il et comment puis-je modifier la commande afin que les invocations suivantes ne créent pas cet effet étrange?

the_velour_fog
la source

Réponses:

16

Vous devez utiliser l' -Toption pour cela, il indique lnde toujours traiter le nom du lien comme le nom du lien souhaité, jamais comme un répertoire.

Sans cette option, si vous donnez lnun nom de lien qui existe en tant que répertoire, il crée un nouveau lien vers la cible dans ce répertoire.

Notez que -Tc'est un GNU-isme (au moins, ce n'est pas dans POSIX), mais vous utilisez déjà -vce qui est aussi un GNU-isme donc j'imagine que ce n'est pas un problème.

Alternativement, vous pouvez simplement spécifier le répertoire parent comme nom de lien, et le lien sera toujours (re) créé là:

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

Cela fonctionne car votre lien symbolique a le même nom que la cible.

Stephen Kitt
la source
merci, oui les options gnu ne sont pas nécessairement portables, mais elles peuvent être vraiment utiles - lorsque la portabilité n'est pas une exigence. votre réponse fonctionne pour moi. Depuis la page de manuel -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 alwayspensez-vous qu'il est préférable de toujours traiter un lien symbolique comme un fichier? J'aurais pensé qu'il valait mieux limiter l'utilisation de ces options "spéciales"?
the_velour_fog
Si vous savez qu'ils sont disponibles, il n'y a aucune raison d'éviter les options spécifiques à GNU (IMO). Vous avez demandé une commande idempotent, -Tc'est comment vous faites lnidempotent. Il existe d'autres façons d'obtenir le même résultat si vous préférez, comme la suppression du lien et sa recréation, ou si vous savez qu'il fooexiste déjà ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(en fait, cela pourrait être une meilleure réponse, je mettrai à jour).
Stephen Kitt
1
cool, oui j'avais précédemment utilisé des ifinstructions pour détecter la présence du lien symbolique et ignorer la commande si le lien symbolique existait, mais les scripts finissent par être encombrés et difficiles à lire, j'allais vers quelque chose d'idempotent mais simple, comme n'utiliser mkdir -paucun test , pas de logique, juste de bons ol one liners :)
the_velour_fog
@Stephen Kitt: vous avez mentionné l'utilisation de -T, mais je ne l'ai pas trouvé dans l'extrait de code de votre réponse. Suis-je incompréhensible ou manque quelque chose?
Guizmoo03
@ Guizmoo03 vous avez peut-être manqué la "variante" qui présente l'extrait de code. Les trois premiers paragraphes expliquent -T, mais la fin de ma réponse présente une autre solution qui n'utilise pas -T.
Stephen Kitt
-1

Le mieux que je puisse dire, c'est que lors de la deuxième passe, la lncommande se comporte comme cpou mv, ce qui signifie que si elle voit un "répertoire" à <destination>, elle y mettra le fichier, sinon si <destination> ne le fait pas existe, il fera <destination>. (c'est ce que l'astuce "slashdot" évite - c'est-à-dire qu'il s'assurera que la <destination> existe et est un répertoire).
Mais je peux me tromper.

Dans les deux cas, cela semble fonctionner

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