diff options
author | kate.ward <kate.ward@forestent.com> | 2009-03-30 18:54:36 +0000 |
---|---|---|
committer | kate.ward <kate.ward@forestent.com> | 2009-03-30 18:54:36 +0000 |
commit | c521068ebb95298c61d714d1961d577ff86c58a7 (patch) | |
tree | b3c58f63d6cd0da21e8fbc95d38f0a9d821958c3 | |
parent | c4b0ef7a998796dee6095d61120e6249488cb908 (diff) | |
download | shflags-c521068ebb95298c61d714d1961d577ff86c58a7.tar.gz |
- fixed issue# 7 where long flags declared with '=' didn't work
- flag and non-flag arguments can now be mixed on the command-line
- removed setting and unsetting of '-u' to check for unset variables
- obsoleted FLAGS_ARGC and replaced with FLAGS_ARGV
-rw-r--r-- | source/1.0/doc/CHANGES-1.0.txt | 25 | ||||
-rw-r--r-- | source/1.0/doc/RELEASE_NOTES-1.0.3.txt | 94 | ||||
-rw-r--r-- | source/1.0/doc/contributors.txt | 5 | ||||
-rwxr-xr-x | source/1.0/examples/debug_output.sh | 47 | ||||
-rwxr-xr-x | source/1.0/examples/hello_world.sh | 3 | ||||
-rwxr-xr-x | source/1.0/examples/write_date.sh | 48 | ||||
-rw-r--r-- | source/1.0/src/shflags | 75 | ||||
-rwxr-xr-x | source/1.0/src/shflags_test_parsing.sh | 52 |
8 files changed, 272 insertions, 77 deletions
diff --git a/source/1.0/doc/CHANGES-1.0.txt b/source/1.0/doc/CHANGES-1.0.txt index 1eac214..f1375ac 100644 --- a/source/1.0/doc/CHANGES-1.0.txt +++ b/source/1.0/doc/CHANGES-1.0.txt @@ -1,6 +1,31 @@ Changes in shFlags 1.0.x ======================== +Changes with 1.0.3 +------------------ + +MAJOR CHANGE! FLAGS_ARGC is now obsolete, and is replaced by FLAGS_ARGV. + +Fixed issue# 7 where long flags defined with '=' (e.g. --abc=123) made it +impossible for the user to know how many non-flag command-line arguments were +available because the value returned by FLAGS_ARGC was wrong. The FLAGS_ARGC +value is now obsolete, but will be maintained for backwards compatibility. The +new method of getting the non-flag arguments is by executing 'eval set -- +"${FLAGS_ARGV}"' after the FLAGS command. The arguments will then be available +using the standard shell $#, $@, $*, $1, etc. variables. + +Due to above fix for issue# 7, there is now proper support for mixing flags +with non-flag arguments on the command-line. Previously, all non-flag arguments +had to be at the end of the command-line. + +Renamed _flags_standardGetopt() and _flags_enhancedGetopt() functions to +_flags_getoptStandard() and _flags_getoptEnhanced(). + +Took out the setting and restoration of the '-u' shell flag to treat unset +variables as an error. No point in having it in this library as it is verified +in the unit tests, and provides basically no benefit. + + Changes with 1.0.2 ------------------ diff --git a/source/1.0/doc/RELEASE_NOTES-1.0.3.txt b/source/1.0/doc/RELEASE_NOTES-1.0.3.txt new file mode 100644 index 0000000..637a18d --- /dev/null +++ b/source/1.0/doc/RELEASE_NOTES-1.0.3.txt @@ -0,0 +1,94 @@ +------------------------------ +shFlags.sh 1.0.3 Release Notes +------------------------------ + +Preface +======= +Copyright 2009 Kate Ward. All Rights Reserved. +Released under the LGPL (GNU Lesser General Public License) +Author: Kate Ward (kate.ward@forestent.com) + +This document covers any known issues and workarounds for the stated release of +shFlags. + +Release info +============ + +This is a major bugfix release of shFlags. The biggest fix is in how non-flag +arguments are made available to the script. + +Major changes +------------- + +The use of the ``FLAGS_ARGC`` variable is now obsolete. It will be maintained +for backwards compatibility with old scripts, but its value is known to be +wrong when flag and non-flag arguments are mixed together on the command-line. + +To gain access to the non-flag arguments, replace the following snippet of code +in your scripts with the updated version. + +:old: shift ${FLAGS_ARGC} +:new: eval set -- "${FLAGS_ARGV}" + +Please see the CHANGES-1.0.txt file for a complete list of changes. + +Obsolete items +-------------- + +Bug fixes +--------- + +Issue# 7 Flags set with '=' result in off-by-one shifting error + +General info +============ + +The unit tests +-------------- + +shFlags is designed to work on as many environments as possible, but not all +environments are created equal. As such, not all of the unit tests will succeed +on every platform. The unit tests are therefore designed to fail, indicating to +the tester that the supported functionality is not present, but an additional +test is present to verify that shFlags properly caught the limitation and +presented the user with an appropriate error message. + +shFlags tries to support both the standard and enhanced versions of ``getopt``. +As each responds differently, and not everything is supported on the standard +version, some unit tests will be skipped (i.e. ASSERTS will not be thrown) when +the standard version of ``getopt`` is detected. The reason being that there is +no point testing for functionality that is positively known not to exist. A +tally of skipped tests will be kept for later reference. + +Standard vs Enhanced getopt +--------------------------- + +Here is a matrix of the supported features of the various **getopt** variants. + ++-------------------------+---+---+ +|Feature |std|enh| ++=========================+===+===+ +|short option names | Y | Y | +|long option names | N | Y | +|spaces in string options | N | Y | ++-------------------------+---+---+ + +Known Issues +------------ + +The **getopt** version provided by default with all versions of Mac OS X (up to +and including 10.5.6) is the standard version. As such, only short flags are +supported. + +The **getopt** version provided by default with all versions of Solaris (up to +and including Solaris 10 and OpenSolaris) is the standard version. As such, +only short flags are supported. + +Workarounds +----------- + +The Zsh shell requires the ``shwordsplit`` option to be set, and the special +``FLAGS_PARENT`` variable must be defined. + + +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 diff --git a/source/1.0/doc/contributors.txt b/source/1.0/doc/contributors.txt index a097f78..146c0c0 100644 --- a/source/1.0/doc/contributors.txt +++ b/source/1.0/doc/contributors.txt @@ -1 +1,4 @@ -- Maciej Bliziński <blizinski@google.com> -- many code reviews +I'd like to thank these people for their contributisons to shFlags. + +Maciej Bliziński <blizinski@google.com> -- _many_ code reviews +Bjarni Einarsson <bre@google.com> -- bug reports diff --git a/source/1.0/examples/debug_output.sh b/source/1.0/examples/debug_output.sh index ed552ef..d54635f 100755 --- a/source/1.0/examples/debug_output.sh +++ b/source/1.0/examples/debug_output.sh @@ -10,17 +10,54 @@ # # This script demonstrates the use of a boolean flag to enable custom # functionality in a script. +# +# Try running these: +# $ ./debug_output.sh speak +# $ ./debug_output.sh sing +# $ ./debug_output.sh --debug sing # source shflags . ../src/shflags -debug() { [ ${FLAGS_debug} -eq ${FLAGS_TRUE} ] && echo "DEBUG: $@" >&2; } - # define flags DEFINE_boolean 'debug' false 'enable debug mode' 'd' +FLAGS_HELP=`cat <<EOF +commands: + speak: say something + sing: sing something +EOF` + + +debug() +{ + [ ${FLAGS_debug} -eq ${FLAGS_TRUE} ] || return + echo "DEBUG: $@" >&2 +} + +die() { + [ $# -gt 0 ] && echo "error: $@" >&2 + flags_help + exit 1 +} + # parse the command-line -FLAGS "$@" || exit 1; shift ${FLAGS_ARGC} +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" + +command=$1 +case ${command} in + '') die ;; + + speak) + debug "I'm getting ready to say something..." + echo 'The answer to the question "What is the meaning of life?" is "42".' + ;; + + sing) + debug "I'm getting ready to sing something..." + echo 'I love to sing! La diddy da dum!' + ;; -debug 'debug mode enabled' -echo 'something interesting' + *) die "unrecognized command (${command})" ;; +esac diff --git a/source/1.0/examples/hello_world.sh b/source/1.0/examples/hello_world.sh index d04a797..6fb2cb0 100755 --- a/source/1.0/examples/hello_world.sh +++ b/source/1.0/examples/hello_world.sh @@ -19,6 +19,7 @@ DEFINE_string 'name' 'world' 'name to say hello to' 'n' # parse the command-line -FLAGS "$@" || exit 1; shift ${FLAGS_ARGC} +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" echo "Hello, ${FLAGS_name}!" diff --git a/source/1.0/examples/write_date.sh b/source/1.0/examples/write_date.sh index 0d9ccd0..85695cf 100755 --- a/source/1.0/examples/write_date.sh +++ b/source/1.0/examples/write_date.sh @@ -14,34 +14,46 @@ # - direct calling of the flags_help() function for script controlled usage # output # - handling of non-flag type command-line arguments that follow the flags +# +# Try the following: +# $ ./write_date.sh now.out +# $ cat now.out +# +# $ ./write_date.sh now.out +# $ cat now.out +# +# $ ./write_date.sh -f now.out +# $ cat now.out # source shflags . ../src/shflags -write_date() { date >"$1"; } - # configure shflags DEFINE_boolean 'force' false 'force overwriting' 'f' FLAGS_HELP="USAGE: $0 [flags] filename" -# parse the command-line -FLAGS "$@" || exit 1; shift ${FLAGS_ARGC} -# check for filename -if [ $# -eq 0 ]; then - echo 'error: filename missing' >&2 +write_date() +{ + date >"$1" +} + +die() +{ + [ $# -gt 0 ] && echo "error: $@" >&2 flags_help exit 1 -fi +} + + +# parse the command-line +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" + +# check for filename +[ $# -gt 0 ] || die 'filename missing' filename=$1 -if [ ! -f "${filename}" ]; then - write_date "${filename}" -else - if [ ${FLAGS_force} -eq ${FLAGS_TRUE} ]; then - write_date "${filename}" - else - echo 'warning: filename exists; not overwriting' >&2 - exit 2 - fi -fi +[ -f "${filename}" -a ${FLAGS_force} -eq ${FLAGS_FALSE} ] \ + && die 'filename exists; not overwriting' +write_date "${filename}" diff --git a/source/1.0/src/shflags b/source/1.0/src/shflags index 304e6db..805b07a 100644 --- a/source/1.0/src/shflags +++ b/source/1.0/src/shflags @@ -4,7 +4,8 @@ # Copyright 2008 Kate Ward. All Rights Reserved. # Released under the LGPL (GNU Lesser General Public License) # -# shFlags -- Advanced command-line flag library for Unix shell scripts +# shFlags -- Advanced command-line flag library for Unix shell scripts. +# http://code.google.com/p/shflags/ # # Author: kate.ward@forestent.com (Kate Ward) # @@ -37,12 +38,17 @@ # # EXAMPLE USAGE: # +# -- begin hello.sh -- # #! /bin/sh # . ./shflags -# -# DEFINE_string name 'world' "somebody's name" -# FLAGS "$@" || exit $?; shift ${FLAGS_ARGC} +# DEFINE_string name 'world' "somebody's name" n +# FLAGS "$@" || exit $? +# eval set -- "${FLAGS_ARGV}" # echo "Hello, ${FLAGS_name}." +# -- end hello.sh -- +# +# $ ./hello.sh -n Kate +# Hello, Kate. # # NOTE: Not all systems include a getopt version that supports long flags. On # these systems, only short flags are recognized. @@ -72,7 +78,7 @@ # return if FLAGS already loaded [ -n "${FLAGS_VERSION:-}" ] && return 0 -FLAGS_VERSION='1.0.2' +FLAGS_VERSION='1.0.3pre' FLAGS_TRUE=0 FLAGS_FALSE=1 @@ -97,17 +103,6 @@ if [ -n "${ZSH_VERSION:-}" ]; then fi fi -# shell flags -# u: treat unset variables as an error when performing parameter expansion -__FLAGS_SHELL_FLAGS='u' - -# save the current set of shell flags, and then set some for ourself -__flags_oldShellFlags=$- -for __flags_shellFlag in `echo "${__FLAGS_SHELL_FLAGS}" |sed 's/\(.\)/\1 /g'` -do - set "-${__flags_shellFlag}" -done - # # constants # @@ -121,6 +116,7 @@ getopt >/dev/null 2>&1 case $? in 0) __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} ;; # bsd getopt 2) + # TODO(kward): look into '-T' option to test the internal getopt() version if [ "`getopt --version`" = '-- ' ]; then __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} else @@ -183,16 +179,10 @@ __flags_boolNames=' ' # space separated list of boolean flag names __flags_longNames=' ' # space separated list of long flag names __flags_shortNames=' ' # space separated list of short flag names +__flags_argc=0 # count of non-flag arguments __flags_columns='' # screen width in columns __flags_opts='' # temporary storage for parsed getopt flags -# restore the previous set of shell flags -for __flags_shellFlag in ${__FLAGS_SHELL_FLAGS}; do - echo ${__flags_oldShellFlags} \ - |grep ${__flags_shellFlag} >/dev/null || set +${__flags_shellFlag} -done -unset __flags_oldShellFlags __flags_shellFlag - #------------------------------------------------------------------------------ # private functions # @@ -357,7 +347,7 @@ _flags_genOptStr() ${_flags_flag_} ${__FLAGS_INFO_SHORT_STR}` if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then _flags_opts_="${_flags_opts_}${_flags_shortName_}" - # getopt needs a trailing ':' to indicate a needed option + # getopt needs a trailing ':' to indicate a required argument [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \ _flags_opts_="${_flags_opts_}:" fi @@ -365,7 +355,7 @@ _flags_genOptStr() ${__FLAGS_OPTSTR_LONG}) _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_flag_}" - # getopt needs a trailing ':' to indicate a needed option + # getopt needs a trailing ':' to indicate a required argument [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \ _flags_opts_="${_flags_opts_}:" ;; @@ -554,7 +544,7 @@ _flags_validateInteger() # @: varies: command-line options to parse # Returns: # integer: a FLAGS success condition -_flags_standardGetopt() +_flags_getoptStandard() { flags_return=${FLAGS_TRUE} _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` @@ -593,7 +583,7 @@ _flags_standardGetopt() # @: varies: command-line options to parse # Returns: # integer: a FLAGS success condition -_flags_enhancedGetopt() +_flags_getoptEnhanced() { flags_return=${FLAGS_TRUE} _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` @@ -627,7 +617,6 @@ _flags_enhancedGetopt() # integer: a FLAGS success condition _flags_parseGetopt() { - FLAGS_ARGC=0 flags_return=${FLAGS_TRUE} if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then @@ -637,6 +626,13 @@ _flags_parseGetopt() eval set -- "$@" fi + # provide user with number of arguments to shift by later + # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not + # properly give user access to non-flag arguments mixed in between flag + # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only + # for backwards compatibility reasons. + FLAGS_ARGC=`expr $# - 1 - ${__flags_argc}` + # handle options. note options with values must do an additional shift while true; do _flags_opt_=$1 @@ -743,15 +739,16 @@ _flags_parseGetopt() fi fi - # shift the option out + # shift the option and non-boolean arguements out. shift - FLAGS_ARGC=`expr ${FLAGS_ARGC} + 1` + [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ] && shift + done - # additional shift for the argument - if [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ]; then - shift - FLAGS_ARGC=`expr ${FLAGS_ARGC} + 1` - fi + # give user back non-flag arguments + FLAGS_ARGV='' + while [ $# -gt 0 ]; do + FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'" + shift done unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \ @@ -795,12 +792,14 @@ FLAGS() [ -z "${__flags_help_type:-}" ] && \ DEFINE_boolean 'help' false 'show this help' 'h' + # record original number of args for use elsewhere + __flags_argc=$# + # parse options - # TODO(kward): look into '-T' option to test the internal getopt() version if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then - _flags_standardGetopt "$@" + _flags_getoptStandard "$@" else - _flags_enhancedGetopt "$@" + _flags_getoptEnhanced "$@" fi flags_return=$? diff --git a/source/1.0/src/shflags_test_parsing.sh b/source/1.0/src/shflags_test_parsing.sh index c027522..bc5e534 100755 --- a/source/1.0/src/shflags_test_parsing.sh +++ b/source/1.0/src/shflags_test_parsing.sh @@ -10,6 +10,7 @@ # shFlags unit test for the flag definition methods # # TODO(kward): assert on FLAGS errors +# TODO(kward): testNonStandardIFS() # load test helpers . ./shflags_test_helpers @@ -20,12 +21,12 @@ testStandardGetopt() { - _flags_standardGetopt '-b' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptStandard '-b' >"${stdoutF}" 2>"${stderrF}" rslt=$? assertTrue "didn't parse valid flag 'b'" ${rslt} th_showOutput ${rslt} "${stdoutF}" "${stderrF}" - _flags_standardGetopt '-x' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptStandard '-x' >"${stdoutF}" 2>"${stderrF}" assertFalse "parsed invalid flag 'x'" $? } @@ -33,14 +34,14 @@ testEnhancedGetopt() { flags_getoptIsEnh || startSkipping - _flags_enhancedGetopt '-b' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptEnhanced '-b' >"${stdoutF}" 2>"${stderrF}" assertTrue "didn't parse valid flag 'b'" $? - _flags_enhancedGetopt '--bool' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptEnhanced '--bool' >"${stdoutF}" 2>"${stderrF}" assertTrue "didn't parse valid flag 'bool'" $? - _flags_enhancedGetopt '-x' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptEnhanced '-x' >"${stdoutF}" 2>"${stderrF}" assertFalse "parsed invalid flag 'x'" $? - _flags_enhancedGetopt '--xyz' >"${stdoutF}" 2>"${stderrF}" + _flags_getoptEnhanced '--xyz' >"${stdoutF}" 2>"${stderrF}" assertFalse "parsed invalid flag 'xyz'" $? } @@ -231,15 +232,15 @@ _testMultipleFlags() _testNonFlagArgs() { - argc=$1 + argv=$1 shift - FLAGS $@ - assertTrue 'parse returned value.' $? + FLAGS "$@" + assertTrue 'parse returned non-zero value.' $? - # shift out the parsed arguments to reach those that weren't parsed - shift ${FLAGS_ARGC} - assertSame 'wrong argc value.' ${argc} $# + eval set -- "${FLAGS_ARGV}" + assertEquals 'wrong count of argv arguments returned.' ${argv} $# + assertEquals 'wrong count of argc arguments returned.' 0 ${FLAGS_ARGC} } testSingleNonFlagArg() @@ -254,10 +255,33 @@ testMultipleNonFlagArgs() testMultipleNonFlagStringArgsWithSpaces() { - _testNonFlagArgs 3 argOne 'arg #2' arg3 + _testNonFlagArgs 3 argOne 'arg two' arg3 } -# TODO(kward): testNonStandardIFS() +testFlagsWithEquals() +{ + FLAGS --str='str_flag' 'non_flag' >"${stdoutF}" 2>"${stderrF}" + assertTrue 'FLAGS returned a non-zero result' $? + assertEquals 'string flag not set properly' 'str_flag' "${FLAGS_str}" + th_showOutput ${rtrn} "${stdoutF}" "${stderrF}" + + eval set -- "${FLAGS_ARGV}" + assertEquals 'wrong count of argv arguments returned.' 1 $# + assertEquals 'wrong count of argc arguments returned.' 1 ${FLAGS_ARGC} +} + +testComplicatedCommandLine() +{ + FLAGS -i 1 --str='two' non_flag_1 --float 3 non_flag_two 'non flag 3' \ + >"${stdoutF}" 2>"${stderrF}" + assertTrue 'FLAGS returned a non-zero result' $? + assertEquals 1 ${FLAGS_int} + assertEquals 'two' "${FLAGS_str}" + assertEquals 3 ${FLAGS_float} + + eval set -- "${FLAGS_ARGV}" + assertEquals $# 3 +} #------------------------------------------------------------------------------ # suite functions |