Inspired by Ivan's post http://blog.famzah.net/2013/07/31/using-flock-in-bash-without-invoking-a-subshell I've written a flock wrapper for bash, that uses flock underneath but allows flock -c to work for bash functions.
It can be called just like the regular flock command, with the benefit that the -c invocation is supported for bash functions; so you can use it like this:
flock -o /tmp/process my_thing "$@"
and I strongly recommend the -o option so that the file descriptor used for the lock is not passed to any sub-processes, which could be problematic if long live sub-processes (e.g. re-spawned daemons) keep it open.
It will pass through and invoke the regular flock if the command isn't a bash function, or if you aren't trying to execute a function.
Sadly, it doesn't recognize bash built-in's.
But the good news is, you can use flock -o ...file... on a shell function inside your shell script without having to worry about file descriptors.
# Helper function (in order to preserve $@ in the caller)
# If this isn't used to call a shell function then return 0
# otherwise return $? as the argumment number which represents
# the command/function to be called
_is_standard_flock() {
local args=$#
# find the first argument that doesn't begin with a -
while test $# != 0
do case "$1" in
-*) shift ; continue ;;
*) break ;;
esac
done
# if it is numeric and there are not additional arguments
test $# = 1 -a -n "$1" -a -z "${1//[0-9]}" && return 0
shift
# (skipping -c if present)
# or the following argument is also not a shell function then use the original flock
if test "$1" = "-c"
then declare -F "$2" >/dev/null || return 0
else declare -F "$1" >/dev/null || return 0
fi
# we can't have shifted many args if this is a legitimate use of flock
# so we will be in range of the exit code
return $(( args - $# + 1))
}
# Help function to determine if -o or --close was given in the flock arguments
_wants_close() {
test "${*/#--close/}" != "$*" && return # will also match bogus arguments like --closed
# remove any -- options
set -- "${@/#--*/}"
# look for options with o
test "${*/#-*o/}" != "$*" && return
return 1
}
flock() {
if _is_standard_flock "$@"
then : # do outside the if-clause so bash can optimise exec where possible
else # save the exit code (offset) as $1
set -- $? "$@"
# ${!$1} is the lock file
# ${@:$(($1 + 1))} might be -c
test "${@:$(($1 + 1)):1}" = "-c" && set -- "${@:1:$1}" "${@:$(($1 + 2))}"
if _wants_close "${*:2:$(( $1 - 1))}"
then { set -- "$1" "$_" "${@:2}" ; command flock "${@:3:$(( $1 - 2))}" $2 && eval '"${@:$(( $1 + 2))}"' "$2>&-" ; set -- $? $2 ; command flock -u $2 ; return $1 ; } {_}<"${!1}"
else { set -- "$1" "$_" "${@:2}" ; command flock "${@:3:$(( $1 - 2))}" $2 && "${@:$(( $1 + 2))}" ; set -- $? $2 ; command flock -u $2 ; return $1 ; } {_}<"${!1}"
fi
fi
command flock "$@"
}
x
No comments:
Post a Comment