Reputation: 1810
I would like to know if there is any way a file can be moved only if the destination does not exist - in other words, move only if it does not lead to overwriting.
mv --update
seemed first to be the solution, however, if the timestamp of the source path is newer than the destination, move will overwrite it and all attempts to circumvent this by modifying the timestamp before the move will fail.
I need this behaviour to implement a simple file based lock where existence of a 'lock' file indicates that the lock is acquired.
I use perl for this task, so if perl has this functionality, it would be as helpful. However, I need to ensure that the move operation is atomic.
Upvotes: 2
Views: 2595
Reputation: 63
Note if you use the Greg solution could be a race condition between these two instructions if another program try to open it.
sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
flock $fh, LOCK_EX or die "$0: flock: $!";
Upvotes: 0
Reputation: 58741
perldoc -f
rename
On *NIX systems, rename
is atomic, and satisfies your question/requirements as literally posed. As a matter of practice for lockfiles, however, I often use the O_EXCL|O_CREAT
approach suggested in @gbacon's answer.
Upvotes: 0
Reputation: 139711
But what will you do while someone else has the lock? Quit and try later? Busy-wait?
If you don't need synchronization, then a good bet is sysopen
with the O_EXCL
and O_CREAT
flags set, which will create the file only if it doesn't exist.
use Fcntl qw/ :DEFAULT /;
# ...
sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
or die "$0: sysopen: $!";
But note the following caveat from the Linux open(2)
manual page:
O_EXCL
is only supported on NFS when using NFSv3 or later on kernel 2.6 or later. In environments where NFSO_EXCL
support is not provided, programs that rely on it for performing locking tasks will contain a race condition. Portable programs that want to perform atomic file locking using a lockfile, and need to avoid reliance on NFS support forO_EXCL
, can create a unique file on the same file system (e.g., incorporating hostname and PID), and uselink(2)
to make a link to the lockfile. Iflink(2)
returns 0, the lock is successful. Otherwise, usestat(2)
on the unique file to check if its link count has increased to 2, in which case the lock is also successful.
“I’d rather have a network filesystem than NFS,” as the saying goes, so keep your coordinating processes on the same machine if you can.
You might consider using flock
as in the code below:
#! /usr/bin/perl
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock /;
my $LOCKFILE = "/tmp/mylock";
sub acquire_lock {
sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
flock $fh, LOCK_EX or die "$0: flock: $!";
$fh;
}
sub work {
for (1 .. 2) {
my $fh = acquire_lock;
print "$0: $$ has lock\n";
sleep rand 3;
close $fh or warn "$0: [$$] close: $!";
}
exit;
}
For a demo, the code below forks five children that take turns acquiring the lock:
my $KIDS = 5;
my %pids;
for (1 .. $KIDS) {
my $pid = fork;
die "$0: fork: $!" unless defined $pid;
$pid ? ++$pids{$pid} : work;
}
while (my $pid = wait) {
last if $pid == -1;
warn "$0: unknown child $pid" unless delete $pids{$pid};
}
warn "$0: still alive: " .
join(", " => sort { $a <=> $b } keys %pids) .
"\n"
if keys %pids;
Sample output:
./kidlock: 26644 has lock ./kidlock: 26645 has lock ./kidlock: 26646 has lock ./kidlock: 26645 has lock ./kidlock: 26648 has lock ./kidlock: 26646 has lock ./kidlock: 26647 has lock ./kidlock: 26647 has lock ./kidlock: 26644 has lock ./kidlock: 26648 has lock
Upvotes: 7
Reputation: 213210
mv -n
should do what you want.
From the man page:
-n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)
Upvotes: 0