Last active
May 23, 2021 18:26
-
-
Save gdvalle/bf61b93e06a52e411da0 to your computer and use it in GitHub Desktop.
An atomic file updater in shell.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -u | |
PROG="${0##*/}" | |
# Default temp file suffix | |
SUFFIX="${SUFFIX-.tmp$$}" | |
usage() { | |
printf 'usage: echo foo | %s <file> [lockfile] [tempfile]\n' "$PROG" >&2 | |
printf '\n' | |
printf 'This program performs the following:\n' >&2 | |
printf ' - relays STDIN to tempfile (Default file%s\n' "$SUFFIX" >&2 | |
printf ' - uses flock to ensure a single writer to file\n' >&2 | |
printf ' - moves tempfile onto file\n' >&2 | |
printf 'NOTE: Ensure file and tempfile are on the same filesystem\n' >&2 | |
printf ' for an atomic mv operation!\n' >&2 | |
} | |
lock() { | |
file="$1" | |
lock_file="${2-${file}.lock}" | |
# Create lock file with FD<10 for POSIX compatibility | |
exec 9>"$lock_file" | |
# Acquire lock | |
flock -n 9 \ | |
&& return 0 \ | |
|| return 1 | |
} | |
main() { | |
if [ $# -lt 1 ]; then | |
usage | |
return 1 | |
fi | |
file=$1 | |
lock_file="${2-${file}.lock}" | |
temp_file="${3-${file}${SUFFIX}}" | |
# Get the lock | |
lock "$file" "$lock_file" \ | |
|| return 2 | |
# Deferred cleanup until program exit. | |
# Remove lock file if process is interrupted. | |
trap 'rm -f "$lock_file"' 1 2 3 6 15 | |
# Temp file is always removed. | |
trap 'rm -f "$temp_file"' EXIT | |
# Relay STDIN to a temp file | |
cat > "$temp_file" | |
if [ "$?" -ne 0 ]; then | |
printf "Failed writing to %s\n" "$temp_file" >&2 | |
return 3 | |
fi | |
# Move temp file into place, hopefully atomically | |
mv "$temp_file" "$file" | |
if [ "$?" -ne 0 ]; then | |
printf "Failed moving %s to %s\n" "$temp_file" "$file" >&2 | |
return 4 | |
fi | |
rm "$lock_file" | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment