Какую команду 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'

он создает символическую ссылку на каталог, где aleady символическая ссылка существует, помещает символическую ссылку в настоящий каталог

% 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-изм (по крайней мере, его нет в POSIX), но вы уже используете, -vкоторый тоже является GNU-измом, поэтому я думаю, что это не проблема.

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

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). Вы просили идемпотентную команду, -Tкак вы делаете lnидемпотент. Существуют и другие способы получения того же результата, если вы предпочитаете, например, удаление ссылки и ее повторное создание, или если вы знаете, что она fooуже существует ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(на самом деле это может быть лучший ответ, я обновлю).
Стивен Китт

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

@ Стефен Китт: вы упомянули использование -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.