Estou tentando (o mais próximo possível) alterar atomicamente um link simbólico. Eu tentei:
ln -sf other_dir existing_symlink
Isso apenas coloca o novo link simbólico no diretório para o qual o link_do_sinal existente apontou.
ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink
Isso fez a mesma coisa: moveu o link simbólico para o diretório.
cp -s other_dir existing_symlink
Ele se recusa porque é um diretório.
Eu li que mv -T
foi feito para isso, mas o busybox não tem o -T
flag.
Não vejo como você pode obter a operação atômica. A página de manual para symlink(2)
diz que dá EEXIST
se o destino já existe. Se o kernel não suporta operação atômica, suas limitações de usuário são irrelevantes.
Eu também não vejo como mv -T
ajuda, mesmo se você tiver. Experimente em uma máquina Linux normal, uma com GNU mv:
$ mkdir a b
$ ln -s a z
$ mv -T b z
mv: cannot overwrite non-directory `z' with directory `b'
Acho que você terá que fazer isso em duas etapas: remover o link simbólico antigo e recriá-lo.
Isso pode de fato ser feito atomicamente com rename(2)
, criando primeiro o novo link simbólico com um nome temporário e, em seguida, substituindo o link antigo de forma limpa numa única tentativa. Como o página man afirma:
Se newpath refere-se a um link simbólico, o link será sobrescrito.
No Shell, você faria isso com mv -T
Da seguinte maneira:
$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z
Você pode strace
o último comando para ter certeza de que está realmente usando rename(2)
sob o capô:
$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z") = 0
Observe que acima, tanto mv -T
E strace
são específicos do Linux.
No FreeBSD, use mv -h
Alternadamente.
Continuando de onde Arto parou aqui, isso é perfeitamente possível, mesmo sem mv -T
, você só precisa criar um novo link simbólico com o mesmo nome do diretório de destino e mv
no diretório pai de seu destino:
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 ./
Exemplo de código obtido via ( http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/ )
Você tentou ln -snf
?
A opção -n
sobrescreve o destino em vez de escrever sob ele quando o destino é um link simbólico para um diretório.
Felicidades