Яку команду idempotent можна використати для створення симпосилання, що вказує на каталог?


16

Я хочу ввести команду в скрипт оболонки, який створить символьне посилання на каталог, але цей скрипт можна запускати знову і знову, тому при наступних викликах команда нічого не повинна змінювати.

Ось структура каталогу:

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

Я хочу створити символьне посилання у foo/названих фрагментах, яке вказує на каталог /tmp/test_symlink/repo/resources/snippets.
Тому я бігаю:

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

що дає бажаний результат.

% 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 каталогів, 5 файлів

Однак, коли команда виконується знову,

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

він створює символьне посилання на каталог, де алеади символьного посилання існує, посилає символьне посилання всередині реального каталогу

% 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

чому це відбувається і як я можу змінити команду, щоб наступні виклики не створили цього дивного ефекту?

Відповіді:


16

Ви повинні використовувати -Tдля цього варіант, він говорить, lnщо завжди трактувати ім'я посилання як потрібне ім'я посилання, а не як каталог.

Без цієї опції, якщо ви даєте lnім'я посилання, яке існує у вигляді каталогу, воно створює нове посилання на ціль всередині цього каталогу.

Зауважте, що -Tце GNU-ism (принаймні, це не в POSIX), але ви вже використовуєте, -vякий є GNU-ism, так що я думаю, це не проблема.

Крім того, ви можете просто вказати батьківський каталог як ім'я посилання, і посилання завжди буде (знову) створено там:

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

Це працює, оскільки ваше посилання має те саме ім'я, що і ціль.


дякую, так, параметри gnu не обов'язково є портативними, але вони можуть бути дуже корисними - коли портативність не є обов'язковою умовою. ваша відповідь працює на мене. З чоловічої сторінки -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 alwaysчи вважаєте ви, що краще посилатися на симпосилання як на файл завжди? Я б подумав, що краще обмежити використання цих "спеціальних" варіантів?
the_velour_fog

Якщо ви знаєте, що вони доступні, немає підстав уникати специфічних для GNU опцій (IMO). Ви попросили команду idempotent, -Tяк ви робите lnidempotent. Є й інші способи отримання того ж результату, якщо ви віддаєте перевагу, наприклад, видалення посилання та його повторне створення, або якщо ви знаєте foo, що вже існує ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(насправді це може бути кращою відповіддю, я оновлю).
Стівен Кітт

1
класно, так, я раніше використовував ifзаяви, щоб виявити наявність символьної посилання та пропустити команду, якщо символьна посилання існувала, але сценарії в кінцевому підсумку забиваються і важко читаються, я збирався щось невміле, але просте, як, наприклад, mkdir -pне використовуючи тестів , немає логіки, просто гарні лайнери :)
the_velour_fog

@Stephen Kitt: ви згадали про використання -T, але я не зміг його знайти у фрагменті коду вашої відповіді. Я щось нерозумію чи щось пропускаю?
Guizmoo03

@ Guizmoo03, можливо, ви пропустили "Альтернативно", який представляє фрагмент. Перші три абзаци пояснюють -T, але в кінці моєї відповіді подається ще одне рішення, яке не використовується -T.
Стівен Кітт

-1

Найкраще, що я можу сказати, що тут відбувається, це те, що при другому проході lnкоманда поводиться так, cpабо mvякщо вона бачить "каталог" при <destination>, вона помістить файл всередину, інакше якщо <destination> не існує, він зробить <визначення>. (що дозволяє уникнути трюку "slashdot" - тобто він переконається, що <destination> існує та є каталогом).
Але я можу помилитися.

У будь-якому випадку це, здається, працює

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.