Que comando idempotent posso usar para criar um link simbólico apontando para um diretório?

16

Quero colocar um comando em um script de shell que criará um link simbólico para o diretório, mas esse script pode ser executado repetidamente, portanto, nas invocações subsequentes, o comando não deve alterar nada.

Aqui está a estrutura de diretórios:

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

Eu quero criar um link simbólico foo/chamado snippets que aponta para o diretório /tmp/test_symlink/repo/resources/snippets.
Então eu corro:

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

qual dá o resultado desejado.

% 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 diretórios, 5 arquivos

No entanto, quando o comando é executado novamente,

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

ele cria um link simbólico para um diretório, onde o link simbólico já existe coloca o link simbólico dentro do diretório real

% 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

por que isso está acontecendo e como posso modificar o comando para que invocações subsequentes não criem esse efeito estranho?

the_velour_fog
fonte

Respostas:

16

Você deve usar a -Topção para isso, ele diz lnpara sempre tratar o nome do link como o nome desejado, nunca como um diretório.

Sem essa opção, se você der lnum nome de link que exista como diretório, ele criará um novo link para o destino dentro desse diretório.

Note que -Té um GNU-ism (pelo menos, não está no POSIX), mas você já está usando o -vque é um GNU-ism também, então imagino que isso não seja um problema.

Como alternativa, você pode apenas especificar o diretório pai como o nome do link, e o link sempre será (re) criado lá:

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

Isso funciona porque seu link simbólico tem o mesmo nome que o destino.

Stephen Kitt
fonte
obrigado, sim, as opções do gnu não são necessariamente portáteis, mas podem ser realmente úteis - quando a portabilidade não é um requisito. sua resposta funciona para mim. Na página do manual -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 alwaysvocê acha que é melhor tratar sempre um link simbólico como um arquivo? Eu teria pensado que seria melhor limitar o uso dessas opções "especiais"?
#
Se você sabe que eles estão disponíveis, não há motivo para evitar opções específicas do GNU (IMO). Você pediu um comando idempotente, -Té assim que você faz o lnidempotente. Existem outras maneiras de obter o mesmo resultado, se você preferir, como excluir o link e recriá-lo, ou se você foojá existe, ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(na verdade essa pode ser uma resposta melhor, atualizarei).
Stephen Kitt
1
legal, sim, eu já estava usando ifinstruções para detectar a presença do link simbólico e pular o comando, se o link simbólico existisse, mas os scripts acabam ficando confusos e difíceis de ler, eu estava procurando por algo idempotente, mas simples, como mkdir -pnão usar testes , nenhuma lógica, bom apenas ol forros :)
the_velour_fog
@ Stephen Kitt: você mencionou o uso de -T, mas eu não consegui encontrá-lo no trecho de código de sua resposta. Estou entendendo mal ou faltando alguma coisa?
precisa saber é o seguinte
@ Guizmoo03 talvez você tenha perdido o "Alternativamente", que apresenta o trecho. Os três primeiros parágrafos explicam -T, mas o final da minha resposta apresenta outra solução que não usa -T.
Stephen Kitt
-1

O melhor que posso dizer aqui é que, na segunda passagem, o lncomando se comporta como cpou mv, ou seja, se ele vir um "diretório" em <destination>, ele colocará o arquivo dentro dele, caso contrário, se <destination> não existir, ele fará <destino>. (que é o que o truque "slashdot" evita - isto é, garantirá que o <destino> exista e seja um diretório).
Mas eu posso estar errado.

Em ambos os casos, isso parece funcionar

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