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