Expanding on my comment to h3rrmiller's answer, and following the pattern of Gilles's snippet, here is another method of making a temporary copy, then moving it into place. I believe this method is not vulnerable to any race conditions. It does require the -n
flag on mv
, which is not part of the POSIX spec. However, it does seem to be widely available. (Verified on the GNU implementation, also BusyBox, FreeBSD, OS X.)
(
tmp=$(mktemp "${target}.XXXXXX") && # "mktemp -p" doesn't work on BSD
cp -- "$source" "$tmp" && # you may want to add -p flag
inode=$(ls -di "$tmp" | cut -d' ' -f1) && # see comment below
mv -n -- "$tmp" "$target" && # won't overwrite, but doesn't then exit non-0
if [ "$(ls -di "$target" | cut -d' ' -f1)" != "$inode" ]; then
rm -- "$tmp"; false
fi
)
In Linux, you can get the inode using stat -c'%i' "$tmp"
, but that's not portable.
Instead of verifying the inode, another idea would be to check whether a file still exists at path $tmp
. If the mv -n ...
succeeded then there shouldn't be. (Unless some other process has freshly created $tmp
before you checked to see whether it was missing.)
For efficiency, it'd make sense to add a check if $target
exists at the start of the routine. If we see that it does exist, we can fail immediately, rather than making the copy, trying to move it, then seeing that that failed so removing the copy. This is left as an (easy) exercise.
cp
... – Chris Down Nov 14 '12 at 02:22return
instead ofexit
. What worries me is the lack of atomicity, i.e. there might be race conditions where some other process creates the file between this script doing the check and it doing the copy. – MvG Nov 14 '12 at 07:04[ -e "$file" ] || [ -L "$file" ]
if you want to consider symlinks to unexistent/inaccessible files. And note that there's potential for a race condition there. – Stéphane Chazelas Nov 14 '12 at 09:48mv -n tempfile target
. Verify that inode oftarget
matches the one you read from tempfile, else fail. Suggested improvements? – dubiousjim Nov 14 '12 at 11:26--no-clobber
causes them to skip a file. – Vladimir Panteleev Oct 29 '23 at 16:13