Przenoszenie katalogu atomicznie

Mam dwa katalogi w tym samym katalogu nadrzędnym. Wywołanie katalogu nadrzędnego base oraz katalogów podrzędnych alpha i bravo . Chcę zastąpić alpha bravo. Najprostsza metoda to:

rm -rf alpha
mv bravo alpha

Komenda mv jest atomowa, ale rm-rf nie. Czy istnieje prosty sposób w bash aby atomicznie zastąpić alpha bravo ? Jeśli nie, czy jest jakiś skomplikowany sposób?

Dodatek:

By The by, it ' s nie jest to problem nie do pokonania, jeśli katalog nie istnieje przez krótki okres. Jest tylko jedno miejsce, które próbuje uzyskać dostęp do alfy i sprawdza, czy Alfa istnieje, zanim zrobi coś krytycznego. Jeśli nie, zostanie wyświetlony komunikat o błędzie. Ale byłoby miło, gdyby był na to sposób. :) Może jest jakiś sposób na bezpośrednią modyfikację i-węzłów, czy coś...

Author: paxdiablo, 2008-11-21

15 answers

Możesz to zrobić, jeśli używasz dowiązań symbolicznych:

Załóżmy, że alpha jest dowiązaniem symbolicznym do katalogu alpha_1 i chcemy przełączyć dowiązanie symboliczne na wskazujące na alpha_2. Oto jak to wygląda przed przełącznikiem:

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

Aby Alfa odnosiła się do alpha_2, użyj ln-nsf:

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

Teraz możesz usunąć stary katalog:

$ rm -rf alpha_1

Zauważ, że nie jest to operacja w pełni atomowa, ale dzieje się to bardzo szybko, ponieważ polecenie " ln " zarówno unlinks, jak i natychmiast odtwarza dowiązanie symboliczne. Możesz zweryfikować to zachowanie za pomocą strace:

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

Możesz powtórzyć tę procedurę zgodnie z życzeniem: np. gdy masz nową wersję, alpha_3:

$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2
 15
Author: Doug Currie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-24 15:47:59

Ostatecznym rozwiązaniem jest połączenie symlink-i rename-podejście:

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

Oczywiście, aplikacja uzyskująca dostęp do alfy musi być w stanie poradzić sobie ze zmianami dowiązań symbolicznych w ścieżce.

 38
Author: David Schmitt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-05 00:36:40

Przyjrzałem się rozwiązaniu Davida, które jest w pełni atomowe ... jedynym problemem jest to, że opcja -T dla {[2] } nie jest zgodna z POSIX, więc niektóre systemy POSIX mogą jej nie obsługiwać (FreeBSD, Solaris, itp. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). z niewielką modyfikacją, to podejście może być zmienione tak, aby było w pełni atomowe i przenośne we wszystkich systemach operacyjnych 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 ./

Exaple via: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

 7
Author: mssaxm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-05 15:55:16

Jeśli masz na myśli atomowe w obu operacjach, to nie sądzę. Najbliżej byłoby:

mv alpha delta
mv bravo alpha
rm -rf delta

Ale to wciąż miałoby małe okienko, w którym alfa nie istniała.

Aby zminimalizować prawdopodobieństwo, że cokolwiek spróbuje użyć Alfy, gdy jej nie ma, możesz (jeśli masz uprawnienia):

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

, co znacznie zwiększy priorytet Twojego procesu podczas operacji mv.

Jeśli, jak mówisz w swoim dodatku, jest tylko jedno miejsce, które sprawdza alpha i błędy it jeśli go nie ma, możesz zmienić ten kod na not error natychmiast, ale spróbuj ponownie w krótkim czasie ( łatwo sub-sekunda dla dwóch operacji mv) - te powtórzenia powinny złagodzić każdy problem, chyba że często zastępujesz alpha bardzo.

 6
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 01:06:56

Użyj oddzielnej, gwarantowanej operacji atomowej, aby działać jako SEMAFOR.

Tak więc, jeśli operacje tworzenia i usuwania plików są atomowe:

1) Utwórz plik o nazwie "SEMAFOR".

2) wtedy i tylko wtedy, gdy to się powiedzie (brak konfliktu z istniejącym plikiem), wykonaj operację (albo proces alpha albo przenieś katalog, w zależności od procesu)

3) SEMAFOR rm.

 6
Author: Oddthinking,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 01:33:45

Sekcja SQLite Dokumentacja blokowanie plików i współbieżność w wersji SQLite 3 posiada dobrze napisany opis swojego eskalującego protokołu blokującego, który kontroluje jednoczesne czytanie, ekskluzywne pisanie i wycofywanie po awarii. Niektóre z tych pomysłów mają tu zastosowanie.

 4
Author: seh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-07 23:00:18

To powinno załatwić sprawę:

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

Strace mv-ft bravo alpha pokazuje:

rename("bravo", "alpha")
Co dla mnie wygląda dość atomowo.
 3
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-06 21:50:52

Nawet jeśli uzyskujesz bezpośredni dostęp do i-węzłów, nadal nie będzie możliwości atomicznej wymiany wartości i-węzłów w przestrzeni użytkownika.

 1
Author: Tyler McHenry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 01:19:56

Martwienie się o atomowy charakter operacji jest bez znaczenia. Chodzi o to, że dostęp do alfy przez inne zadanie i tak nie będzie atomowy.

Podejście do semafora Oddthinking to jedyna droga.

Jeśli nie możesz zmodyfikować innego zadania, musisz upewnić się, że nie działa przed wykonaniem wymiany.

 1
Author: Loren Pechtel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 04:17:12

Od Linuksa 3.15, nowe wywołanie systemowe renameat2 może atomicznie wymieniać dwie ścieżki w tym samym systemie plików. Jednakże, nie ma nawet glibc owijarki do niego jeszcze, nie mówiąc już o coreutils sposób, aby uzyskać do niego dostęp. Więc wyglądałoby to mniej więcej tak:

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");

(oczywiście, należy zrobić właściwą obsługę błędów itp. - zobacz Ten gist {[10] } dla bardziej wyrafinowanego renameat2 opakowania.)

To powiedziawszy - rozwiązanie dowiązania symbolicznego wymienione przez innych jest zarówno łatwiejsze, jak i przenośne, więc chyba że bravo już istnieje, a ty musisz atomicznie go zaktualizować, zamiast tego użyj dowiązania symbolicznego.

 1
Author: Lucas Werkmeister,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-15 15:05:25

Nie wierzę, że jest na to jakiś atomowy sposób. Najlepiej zrobić coś takiego:

mv alpha delme
mv bravo alpha
rm -rf delme
 0
Author: Chris Charabaruk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 00:51:34

Należy pamiętać, że jeśli twój proces ma otwarty któryś z plików w alfie, gdy wystąpi ten ruch/usuwanie, proces nie zauważy, a wszelkie zapisane dane zostaną utracone, gdy plik zostanie zamknięty i ostatecznie usunięty.

 0
Author: Brian C. Lane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 03:38:51

Mv i ln mogą być używane do operacji atomowych. Użyłem ln (1) do atomicznego wdrażania aplikacji internetowych.

Poprawnym sposobem zastąpienia dowiązania symbolicznego jest LN-nsf

ln -nsf <target> <link_name>

Np.

$ 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
 0
Author: Philip Reynolds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-16 14:48:00

Możliwe jest również zastąpienie całej części zawartości jednocześnie w jakimś prefiksie (Z tutaj) za pomocą unionfs-fuse:

# mkdir a b c Z
# touch a/1 b/2 c/3
# ln -s a X
# ln -s b Y
# unionfs X=RW:Y=RW Z
# shopt -s globstar
# file **
a:   directory
a/1: empty
b:   directory
b/2: empty
c:   directory
c/3: empty
X:   symbolic link to a
Y:   symbolic link to b
Z:   directory
Z/1: empty
Z/2: empty
# ln -sfn c Y
# file **/*
a:   directory
a/1: empty
b:   directory
b/2: empty
c:   directory
c/3: empty
X:   symbolic link to a
X/1: empty
Y:   symbolic link to c
Y/3: empty
Z:   directory
Z/1: empty
Z/3: empty
# fusermount -u Z
# rm -r a b c X Y Z
 0
Author: Orient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-12 20:09:16

Dlaczego po prostu nie zrobisz czegoś takiego:

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

Oznaczałoby to, że wszystko w alfa zostanie zniszczone, alfa nigdy nie zostanie usunięta, a cała zawartość zostanie przeniesiona.

 -2
Author: Dan Fego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 04:44:07