Jakiego polecenia idempotent mogę użyć, aby utworzyć dowiązanie symboliczne wskazujące katalog?

16

Chcę wstawić polecenie do skryptu powłoki, który utworzy dowiązanie symboliczne do katalogu, ale ten skrypt może być uruchamiany w kółko, więc przy kolejnych wywołaniach polecenie nie powinno niczego zmieniać.

Oto struktura katalogów:

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

Chcę utworzyć dowiązanie symboliczne w foo/nazwanych fragmentach, które wskazują katalog /tmp/test_symlink/repo/resources/snippets.
Więc biegam:

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

co daje pożądany rezultat.

% 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 katalogów, 5 plików

Jednak po ponownym uruchomieniu polecenia

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

tworzy symlink do katalogu, w którym istnieje symlink, umieszcza symlink w prawdziwym katalogu

% 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

dlaczego tak się dzieje i jak mogę zmodyfikować polecenie, aby kolejne wywołania nie wywołały tak dziwnego efektu?

the_velour_fog
źródło

Odpowiedzi:

16

Powinieneś użyć -Tdo tego opcji, mówi ona, lnaby zawsze traktować nazwę łącza jako żądaną nazwę łącza, nigdy jako katalog.

Bez tej opcji, jeśli podasz lnnazwę linku, który istnieje jako katalog, tworzy on nowy link do celu w tym katalogu.

Zauważ, że -Tjest to GNU-ism (przynajmniej nie ma go w POSIX), ale już używasz, -vktóry jest również GNU-ism, więc wyobrażam sobie, że to nie jest problem.

Alternatywnie możesz po prostu podać katalog nadrzędny jako nazwę linku, a link będzie zawsze (ponownie) tam tworzony:

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

Działa to, ponieważ twój dowiązanie symboliczne ma taką samą nazwę jak cel.

Stephen Kitt
źródło
dzięki, tak, opcje GNU niekoniecznie są przenośne, ale mogą być naprawdę przydatne - gdy przenośność nie jest wymagana. twoja odpowiedź działa dla mnie. Ze strony podręcznika -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 alwaysczy uważasz, że lepiej zawsze traktować dowiązanie symboliczne jako plik? Myślałbym, że lepiej byłoby ograniczyć korzystanie z tych „specjalnych” opcji?
the_velour_fog
Jeśli wiesz, że są dostępne, nie ma powodu, aby unikać opcji specyficznych dla GNU (IMO). Prosiłeś o idempotentne polecenie, oto -Tjak lnczynisz idempotentny. Istnieją inne sposoby uzyskania tego samego wyniku, jeśli wolisz, takie jak usunięcie linku i ponowne utworzenie go, lub jeśli wiesz, że foojuż istnieje ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(faktycznie może to być lepsza odpowiedź, zaktualizuję).
Stephen Kitt
1
fajnie, tak. Wcześniej używałem ifinstrukcji do wykrywania obecności dowiązania symbolicznego i pomijałem polecenie, jeśli dowiązanie symboliczne istniało, ale skrypty stają się zagracone i trudne do odczytania, szukałem czegoś idempotentnego, ale prostego, jak mkdir -pbrak testów , bez logiki, po prostu dobre
stare linijki
@Stephen Kitt: wspomniałeś o użyciu -T, ale nie mogłem go znaleźć we fragmencie kodu twojej odpowiedzi. Czy coś nie rozumiem lub czegoś mi brakuje?
Guizmoo03
@ Guizmoo03 być może przegapiłeś „Alternative”, która wprowadza fragment kodu. Pierwsze trzy akapity wyjaśniają -T, ale koniec mojej odpowiedzi przedstawia inne rozwiązanie, z którego nie korzystam -T.
Stephen Kitt
-1

Najlepsze, co mogę powiedzieć, to, co się tutaj dzieje, to że przy drugim przejściu lnpolecenie zachowuje się tak, cplub mvjeśli widzi „katalog” w <cel>, umieści w nim plik, w przeciwnym razie, jeśli <cel> nie istnieje, spowoduje <destination>. (czego unika sztuczka „slashdot” - tzn. upewni się, że <cel> istnieje i jest katalogiem).
Ale mogę się mylić.

W obu przypadkach wydaje się, że to działa

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
the_velour_fog
źródło