Quale comando idempotente posso usare per creare un collegamento simbolico che punta a una directory?


16

Voglio inserire un comando in uno script di shell che creerà un link simbolico alla directory, ma questo script potrebbe essere eseguito più volte, quindi nelle successive chiamate, il comando non dovrebbe cambiare nulla.

Ecco la struttura delle directory:

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

Voglio creare un collegamento simbolico nei foo/frammenti chiamati che punta alla directory /tmp/test_symlink/repo/resources/snippets.
Quindi 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'

che dà il risultato desiderato.

% 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 directory, 5 file

Tuttavia, quando il comando viene eseguito di nuovo,

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

crea un collegamento simbolico a una directory, dove esiste già il collegamento simbolico, inserisce il collegamento simbolico nella directory reale

% 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

perché sta succedendo e come posso modificare il comando in modo che le successive chiamate non creino questo strano effetto?

Risposte:


16

Dovresti usare l' -Topzione per questo, diceln di trattare sempre il nome del collegamento come il nome del collegamento desiderato, mai come una directory.

Senza questa opzione, se lo dai ln un nome di collegamento esistente come directory, viene creato un nuovo collegamento alla destinazione all'interno di tale directory.

Nota che -Tè un GNU-ism (almeno, non è in POSIX), ma che stai già utilizzando-v quale è anche un GNU-ism, quindi immagino che non sia un problema.

In alternativa, puoi semplicemente specificare la directory principale come nome del collegamento e il collegamento verrà sempre (ri) creato lì:

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

Questo funziona perché il tuo link simbolico ha lo stesso nome della destinazione.


grazie, sì, le opzioni di gnu non sono necessariamente portatili, ma possono essere davvero utili - quando la portabilità non è un requisito. la tua risposta funziona per me. Dalla pagina man -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 alwayspensi che sia meglio trattare sempre un symlink come un file? Avrei pensato che sarebbe meglio limitare l'uso di queste opzioni "speciali"?
the_velour_fog

Se sai che sono disponibili, non c'è motivo di evitare le opzioni specifiche GNU (IMO). Hai chiesto un comando idempotente, -Tè come lo fai lnidempotente. Esistono altri modi per ottenere lo stesso risultato se si preferisce, come eliminare il collegamento e ricrearlo, o se si conosce foogià esiste ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(in realtà potrebbe essere una risposta del tutto migliore, aggiornerò).
Stephen Kitt,

1
bello, sì, in precedenza avevo usato le ifistruzioni per rilevare la presenza del collegamento simbolico e saltare il comando se il collegamento simbolico esisteva, ma gli script finiscono per essere ingombra e difficile da leggere, stavo cercando qualcosa di idempotente ma semplice, come mkdir -pnon usare nessun test , nessuna logica, solo buoni
soliti

@Stephen Kitt: hai menzionato l'uso di -T, ma non sono riuscito a trovarlo nello snippet di codice della tua risposta. Sto fraintendendo o perdendo qualcosa?
Guizmoo03,

@ Guizmoo03 forse ti sei perso l '"Alternativamente" che introduce lo snippet. I primi tre paragrafi spiegano -T, ma la fine della mia risposta presenta un'altra soluzione che non utilizza -T.
Stephen Kitt,

-1

La cosa migliore che posso dire è che al secondo passaggio il lncomando si comporta come cpo mv, ovvero se vede una "directory" in <destinazione> inserirà il file al suo interno, altrimenti se <destinazione> non funziona esiste, farà <destinazione>. (che è ciò che evita il trucco "slashdot" - cioè farà in modo che <destinazione> esista e sia una directory).
Ma potrei sbagliarmi.

In entrambi i casi questo sembra funzionare

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.