#!/bin/bashset -eu -o pipefail -o noclobbersemi_atomic_cross_filesystem_copy () { declare src="$1" declare dst="$2" declare dst_tmp="${dst}.part" declare -i tmp_rc= # Ensure noblobber is on for this to work. cat "$src" > "$dst_tmp" || tmp_rc=$? if [[ $tmp_rc -ne 0 ]] ; then # Someone else beat us to it and owns the file. return 2 elif [[ -e "$dst" ]] ; then # Someone else beat us to move the temp file over the destination. rm -v "$dst_tmp" return 2 fi # Move the temporary file to the destination. mv -v "$dst_tmp" "$dst"}main () { declare tgt_dir="/tmp/nicolaw/target_dir" declare src_dir="/tmp/nicolaw/source_dir" declare file # Create some example files. mkdir -pv "$tgt_dir" "$src_dir" for file in {1..10} ; do [[ -e "$src_dir/my$file.dat" ]] \ || dd if=/dev/zero of="$src_dir/my$file.dat" count=1000000 bs=1024 done for file in "$src_dir"/*.dat ; do declare src="$file" declare dst="$tgt_dir/${file##*/}" # Skip if the file exists. if [[ -e "$dst" ]] ; then echo "File $dst already exists." else # Try to copy it semi-atomically. declare -i copy_rc= semi_atomic_cross_filesystem_copy "$src" "$dst" || copy_rc=$? if [[ $copy_rc -eq 0 ]] ; then echo "File $dst copied OK." elif [[ $copy_rc -eq 2 ]] ; then >&2 echo "Copy to $dst already in progress by another process." else >&2 echo "Copy to $dst failed." fi fi done}main "$@"