Вопрос

У меня есть два каталога в одном родительском каталоге.Вызовите родительский каталог База и дочерние каталоги альфа и браво.Я хочу заменить альфа с браво.Самый простой метод - это:

rm -rf alpha
mv bravo alpha

Команда mv является атомарной, а rm -rf - нет.Есть ли простой способ в bash атомарно заменить альфа с браво?Если нет, то есть ли какой-то сложный способ?

ДОБАВЛЕНИЕ:

Кстати, это не является непреодолимой проблемой, если каталог не существует в течение короткого периода.Есть только одно место, которое пытается получить доступ к alpha, и оно проверяет, существует ли alpha, прежде чем делать что-либо критическое.Если нет, то выдается сообщение об ошибке.Но было бы неплохо, если бы существовал способ сделать это.:) Может быть, есть какой-то способ изменить индексные индексы напрямую или что-то в этом роде...

Это было полезно?

Решение

Вы можете сделать это, если используете символические ссылки:

Допустим, alpha - это символическая ссылка на каталог alpha_1, и вы хотите переключить символическую ссылку так, чтобы она указывала на alpha_2.Вот как это выглядит до переключения:

$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Чтобы заставить alpha ссылаться на alpha_2, используйте ln -nsf:

$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Теперь вы можете удалить старый каталог:

$ rm -rf alpha_1

Обратите внимание, что на самом деле это НЕ полностью атомарная операция, но она происходит очень быстро, поскольку команда "ln" одновременно разблокирует, а затем немедленно воссоздает символическую ссылку.Вы можете проверить это поведение с помощью strace:

$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha")             = -1 EEXIST (File exists)
unlink("alpha")                         = 0
symlink("alpha_2", "alpha")             = 0
...

Вы можете повторить эту процедуру по желанию:например ,когда у вас появится новая версия, alpha_3:

$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2

Другие советы

Конечным решением является сочетание подхода с символической ссылкой и переименованием:

mkdir alpha_real
ln -s alpha_real alpha

# now use "alpha"

mkdir beta_real
ln -s beta_real tmp 

# atomically rename "tmp" to "alpha"
# use -T to actually replace "alpha" instead of moving *into* "alpha"
mv -T tmp alpha

Конечно, приложение, обращающееся к alpha, должно иметь возможность обрабатывать символические ссылки, изменяющиеся в пути.

Подхватываю здесь решение Дэвида, которое полностью атомарно ...единственная проблема, с которой вы столкнулись бы, заключается в том, что -T вариант для mv не является POSIX, и поэтому некоторые операционные системы POSIX могут его не поддерживать (FreeBSD, Solaris и т.д.... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html).С небольшими изменениями этот подход может быть изменен, чтобы быть полностью атомарным и переносимым во все операционные системы POSIX:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

пример через: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

Если вы имеете в виду атомарность в обеих операциях, я в это не верю.Самым близким было бы:

mv alpha delta
mv bravo alpha
rm -rf delta

но это все равно оставило бы небольшое окно, в котором альфа не существовала.

Чтобы свести к минимуму вероятность того, что что-либо попытается использовать alpha, пока его там нет, вы могли бы (если у вас есть полномочия):

nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta

что существенно повысит приоритет вашего процесса, в то время как mv происходят операции.

Если, как вы говорите в своем добавлении, есть только одно место, которое проверяет alpha и выдает ошибки, если его там нет, вы могли бы изменить этот код, чтобы он не выдавал ошибок немедленно, но повторите попытку через короткое время (легко менее секунды для двух mv операции) - эти повторные попытки должны устранить любую проблему, если только вы не заменяете alpha очень часто.

Используйте отдельную, гарантированную атомарную операцию в качестве семафора.

Итак, если операции создания и удаления файла являются атомарными:

1) создайте файл под названием "семафор".

2) Если и только если это успешно (нет конфликта с существующим файлом), выполните операцию (либо обработайте alpha, либо переместите каталог, в зависимости от процесса)

3) семафор rm.

В Базы данных SQLite раздел документации Блокировка файлов и параллелизм в SQLite версии 3 имеет хорошо написанное описание своего расширяющегося протокола блокировки для управления одновременным чтением, эксклюзивной записью и откатом после сбоя.Некоторые из этих идей применимы и здесь.

Это должно сработать:

mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha

strace mv - шоу fT bravo alpha показывает:

rename("bravo", "alpha")

что, по-моему, выглядит довольно атомарно.

Начиная с Linux 3.15, новый renameat2 системный вызов может атомарно обмениваться двумя путями в одной файловой системе.Тем не менее, нет даже glibc обертки для него еще нет, не говоря уже о coreutils способ получить к нему доступ.Так что это выглядело бы примерно так:

int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
syscall(SYS_renameat2, dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");

(Конечно, вы должны выполнить надлежащую обработку ошибок и т.д.– видишь эта суть для более утонченного renameat2 обертка.)

Тем не менее, решение с символическими ссылками, упомянутое другими, является более простым и переносимым, поэтому, если bravo уже существует, и вы должен обновите его атомарно, вместо этого используйте символическую ссылку.

Даже если бы вы обращались к индексным дескрипторам напрямую, все равно не было бы способа атомарно поменять местами значения индексных дескрипторов в пользовательском пространстве.

Беспокоиться об атомарной природе операции бессмысленно.Дело в том, что доступ к alpha другой задачей в любом случае не будет атомарным.

Семафорный подход Oddthinking - это единственный возможный путь.

Если вы не можете изменить другую задачу, вам придется убедиться, что она не запущена, прежде чем выполнять замену.

Я не верю, что есть какой-то атомарный способ сделать это.Ваш лучший выбор - сделать что-то вроде этого:

mv alpha delme
mv bravo alpha
rm -rf delme

Следует иметь в виду, что если в вашем процессе какой-либо из файлов в alpha открыт, когда происходит это перемещение / удаление, процесс этого не заметит, и все записанные данные будут потеряны, когда файл будет закрыт и окончательно удален.

mv и ln могут использоваться для атомарных операций.Я использовал ln (1) для атомарного развертывания веб-приложений.

Правильный способ заменить символическую ссылку на ln -nsf

ln -nsf <target> <link_name>

например ,

$ mkdir dir1
$ mkdir dir2
$ ln -s dir1 mylink
$ ls -l mylink
lrwxrwxrwx  1 phil phil 4 Nov 16 14:45 mylink -> dir1
$ ln -nsf dir2 mylink
$ ls -l mylink
lrwxrwxrwx  1 phil phil 4 Nov 16 14:46 mylink -> dir2

Также возможно заменить сразу целую часть содержимого в каком-нибудь префиксе (Z здесь), используя unionfs-fuse:

Почему бы тебе просто не сделать что-нибудь вроде:

rm -rf alpha/*
mv bravo/* alpha/
rm -rf bravo/

Это означало бы , что все в альфа уничтожен, альфа никогда удаляется, и все содержимое перемещается.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top