diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..30ef640 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + + +build: + os: ubuntu-24.04 + tools: + python: "3.12" + # You can also specify other tool versions: + + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: doc/conf.py diff --git a/ChangeLog b/ChangeLog index 22fe098..3461a17 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,16 @@ -2.10.1 (TBA) +2.11.0 (2024-05-??) ------------------- +Buxfixes: + +* `#!/bin/bash` is no longer the shebang. The more portable `#!/usr/bin/env bash` is used instead (#185). +* Indentation is consistent in validation functions (#143) +* Fixed bug preventing short options to have capital letters (#199) + +New features: + +* The new Argbash macro `ARGBASH_INDICATE_SUPPLIED` allows you to get information on whether an argument has been explicitly passed on the commandline. Big thanks to Kevin Stravers (@kstrafe) for his heroic effort! +* A new `excised` mode that doesn't contain generated code has been introduced (#186) 2.10.0 (2020-09-22) ------------------- diff --git a/README.md b/README.md index 535bd83..0b7048a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,11 @@ * But they don't since arguments support is a daunting task, because ... * `getopt` is discouraged, `getopts` doesn't support long options, there is no widely-accepted `Bash` module to do the task and some solutions don't work on all platforms (Linux, OSX, MSW)... -Give `Argbash` a try and stop being terrorized by those pesky arguments! With Argbash, you will get: +Having Argbash, you can generate interface for your first script in a matter of seconds. See a [termtosvg](https://github.com/nbedos/termtosvg) recording of how `head.sh`, a script with a subset of the `head` command's interface, is created by editing a template provided by `argbash-init`. + +![Example](./resources/screencast.svg) + +Definitely do give `Argbash` a try, and stop being terrorized by those pesky arguments! With Argbash, you will get: * Fast, minimalistic declaration of arguments your script expects (see below for supported argument types). * Scripts generated from definitions once that can be used on all platforms that have `bash`. diff --git a/doc/guide.rst b/doc/guide.rst index b963684..4a62f0d 100644 --- a/doc/guide.rst +++ b/doc/guide.rst @@ -626,8 +626,10 @@ Plus, there are convenience macros: * The user forgets to supply value to an optional argument, so the next argument is mistaken for it. For example, when we leave ``time`` from ``ls --sort time --long /home/me/*``, we get a syntactically valid command-line ``ls --sort --long /home/me/*``, where ``--long`` is identified as value of the argument ``--sort`` instead an argument on its own. + As ``--long`` is a supported argument of ``ls``, both ``no-any-options`` or ``no-local-options`` would catch this error. * The user intends to pass an optional argument on the command-line (e.g. ``--sort``), but makes a typo, (e.g. ``--srot``), or the script actually doesn't support that argument. As an unwanted consequence, it is interpreted as a positional argument. + As ``--srot`` is not a supported argument of ``ls``, only ``no-any-options`` would catch this error. * Make Argbash-powered scripts getopts-compatible: diff --git a/resources/screencast.svg b/resources/screencast.svg new file mode 100644 index 0000000..6f552fa --- /dev/null +++ b/resources/screencast.svg @@ -0,0 +1,813 @@ + + + + + + + + + + + [matej@matej-mrazak tmp]$ [matej@matej-mrazak tmp]$ a [matej@matej-mrazak tmp]$ ar [matej@matej-mrazak tmp]$ arg [matej@matej-mrazak tmp]$ argb [matej@matej-mrazak tmp]$ argba [matej@matej-mrazak tmp]$ argbas [matej@matej-mrazak tmp]$ argbash [matej@matej-mrazak tmp]$ argbash- [matej@matej-mrazak tmp]$ argbash-i [matej@matej-mrazak tmp]$ argbash-in [matej@matej-mrazak tmp]$ argbash-ini [matej@matej-mrazak tmp]$ argbash-init [matej@matej-mrazak tmp]$ argbash-init - [matej@matej-mrazak tmp]$ argbash-init -- [matej@matej-mrazak tmp]$ argbash-init --p [matej@matej-mrazak tmp]$ argbash-init --po [matej@matej-mrazak tmp]$ argbash-init --pos [matej@matej-mrazak tmp]$ argbash-init --pos [matej@matej-mrazak tmp]$ argbash-init --pos f [matej@matej-mrazak tmp]$ argbash-init --pos fi [matej@matej-mrazak tmp]$ argbash-init --pos fil [matej@matej-mrazak tmp]$ argbash-init --pos file [matej@matej-mrazak tmp]$ argbash-init --pos file [matej@matej-mrazak tmp]$ argbash-init --pos file - [matej@matej-mrazak tmp]$ argbash-init --pos file -- [matej@matej-mrazak tmp]$ argbash-init --pos file --o [matej@matej-mrazak tmp]$ argbash-init --pos file --op [matej@matej-mrazak tmp]$ argbash-init --pos file --opt [matej@matej-mrazak tmp]$ argbash-init --pos file --opt [matej@matej-mrazak tmp]$ argbash-init --pos file --opt l [matej@matej-mrazak tmp]$ argbash-init --pos file --opt li [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lin [matej@matej-mrazak tmp]$ argbash-init --pos file --opt line [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines h [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines he [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines hea [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines head [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines head. [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines head.s [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines head.sh [matej@matej-mrazak tmp]$ argbash-init --pos file --opt lines head.sh [matej@matej-mrazak tmp]$ n [matej@matej-mrazak tmp]$ na [matej@matej-mrazak tmp]$ nan [matej@matej-mrazak tmp]$ nano [matej@matej-mrazak tmp]$ nano [matej@matej-mrazak tmp]$ nano h [matej@matej-mrazak tmp]$ nano he [matej@matej-mrazak tmp]$ nano hea [matej@matej-mrazak tmp]$ nano head [matej@matej-mrazak tmp]$ nano head. [matej@matej-mrazak tmp]$ nano head.sh [matej@matej-mrazak tmp]$ nano head.sh GNU nano 8.4 head.sh #!/bin/bash # m4_ignore(echo "This is just a script template, not the script (yet) - pass it to 'argbas>exit 11 #)Created by argbash-init v2.10.0# ARG_OPTIONAL_SINGLE([lines])# ARG_POSITIONAL_SINGLE([file])# ARG_DEFAULTS_POS# ARG_HELP([<The general help message of my script>])# ARGBASH_GO# [ <-- needed because of Argbash# vvv PLACE YOUR CODE HERE vvv# For example:printf 'Value of --%s: %s\n' 'lines' "$_arg_lines"printf "Value of '%s': %s\\n" 'file' "$_arg_file"# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^[ Read 21 lines ]^G Help^O Write Out ^F Where Is ^K Cut^T Execute ^C Location^X Exit^R Read File ^\ Replace ^U Paste ^J Justify ^/ Go To Line#!/bin/bash # m4_ignore(echo "This is just a script template, not the script (yet) - pass it to 'argbas>exit 11 #)Created by argbash-init v2.10.0# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines]) # ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines])# ARG_OPTIONAL_SINGLE([lines]) GNU nano 8.4 head.sh Modified # ARG_OPTIONAL_SINGLE([lines],)# ARG_OPTIONAL_SINGLE([lines],)# ARG_OPTIONAL_SINGLE([lines], )# ARG_OPTIONAL_SINGLE([lines], )# ARG_OPTIONAL_SINGLE([lines], n)# ARG_OPTIONAL_SINGLE([lines], n)# ARG_OPTIONAL_SINGLE([lines], n,)# ARG_OPTIONAL_SINGLE([lines], n,)# ARG_OPTIONAL_SINGLE([lines], n, )# ARG_OPTIONAL_SINGLE([lines], n, )# ARG_OPTIONAL_SINGLE([lines], n, [)# ARG_OPTIONAL_SINGLE([lines], n, [)# ARG_OPTIONAL_SINGLE([lines], n, [h)# ARG_OPTIONAL_SINGLE([lines], n, [h)# ARG_OPTIONAL_SINGLE([lines], n, [ho)# ARG_OPTIONAL_SINGLE([lines], n, [ho)# ARG_OPTIONAL_SINGLE([lines], n, [how)# ARG_OPTIONAL_SINGLE([lines], n, [how)# ARG_OPTIONAL_SINGLE([lines], n, [how )# ARG_OPTIONAL_SINGLE([lines], n, [how )# ARG_OPTIONAL_SINGLE([lines], n, [how m)# ARG_OPTIONAL_SINGLE([lines], n, [how m)# ARG_OPTIONAL_SINGLE([lines], n, [how ma)# ARG_OPTIONAL_SINGLE([lines], n, [how ma)# ARG_OPTIONAL_SINGLE([lines], n, [how man)# ARG_OPTIONAL_SINGLE([lines], n, [how man)# ARG_OPTIONAL_SINGLE([lines], n, [how many)# ARG_OPTIONAL_SINGLE([lines], n, [how many)# ARG_OPTIONAL_SINGLE([lines], n, [how many )# ARG_OPTIONAL_SINGLE([lines], n, [how many )# ARG_OPTIONAL_SINGLE([lines], n, [how many l)# ARG_OPTIONAL_SINGLE([lines], n, [how many l)# ARG_OPTIONAL_SINGLE([lines], n, [how many li)# ARG_OPTIONAL_SINGLE([lines], n, [how many li)# ARG_OPTIONAL_SINGLE([lines], n, [how many lin)# ARG_OPTIONAL_SINGLE([lines], n, [how many lin)# ARG_OPTIONAL_SINGLE([lines], n, [how many line)# ARG_OPTIONAL_SINGLE([lines], n, [how many line)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines])# ARG_OPTIONAL_SINGLE([lines], n, [how many lines])# ARG_OPTIONAL_SINGLE([lines], n, [how many lines],)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines],)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], )# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], )# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], 1)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], 1)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], 10)# ARG_OPTIONAL_SINGLE([lines], n, [how many lines], 10)# ARG_POSITIONAL_SINGLE([file]) # ARG_POSITIONAL_SINGLE([file])# ARG_POSITIONAL_SINGLE([file],)# ARG_POSITIONAL_SINGLE([file],)# ARG_POSITIONAL_SINGLE([file], )# ARG_POSITIONAL_SINGLE([file], )# ARG_POSITIONAL_SINGLE([file], [)# ARG_POSITIONAL_SINGLE([file], [)# ARG_POSITIONAL_SINGLE([file], [f)# ARG_POSITIONAL_SINGLE([file], [f)# ARG_POSITIONAL_SINGLE([file], [fi)# ARG_POSITIONAL_SINGLE([file], [fi)# ARG_POSITIONAL_SINGLE([file], [fil)# ARG_POSITIONAL_SINGLE([file], [fil)# ARG_POSITIONAL_SINGLE([file], [file)# ARG_POSITIONAL_SINGLE([file], [file)# ARG_POSITIONAL_SINGLE([file], [file )# ARG_POSITIONAL_SINGLE([file], [file )# ARG_POSITIONAL_SINGLE([file], [file t)# ARG_POSITIONAL_SINGLE([file], [file t)# ARG_POSITIONAL_SINGLE([file], [file to)# ARG_POSITIONAL_SINGLE([file], [file to)# ARG_POSITIONAL_SINGLE([file], [file to )# ARG_POSITIONAL_SINGLE([file], [file to )# ARG_POSITIONAL_SINGLE([file], [file to c)# ARG_POSITIONAL_SINGLE([file], [file to c)# ARG_POSITIONAL_SINGLE([file], [file to co)# ARG_POSITIONAL_SINGLE([file], [file to co)# ARG_POSITIONAL_SINGLE([file], [file to con)# ARG_POSITIONAL_SINGLE([file], [file to con)# ARG_POSITIONAL_SINGLE([file], [file to cons)# ARG_POSITIONAL_SINGLE([file], [file to cons)# ARG_POSITIONAL_SINGLE([file], [file to consi)# ARG_POSITIONAL_SINGLE([file], [file to consi)# ARG_POSITIONAL_SINGLE([file], [file to consid)# ARG_POSITIONAL_SINGLE([file], [file to consid)# ARG_POSITIONAL_SINGLE([file], [file to conside)# ARG_POSITIONAL_SINGLE([file], [file to conside)# ARG_POSITIONAL_SINGLE([file], [file to consider)# ARG_POSITIONAL_SINGLE([file], [file to consider)# ARG_POSITIONAL_SINGLE([file], [file to consider])# ARG_POSITIONAL_SINGLE([file], [file to consider])# ARG_POSITIONAL_SINGLE([file], [file to consider],)# ARG_POSITIONAL_SINGLE([file], [file to consider],)# ARG_POSITIONAL_SINGLE([file], [file to consider], )# ARG_POSITIONAL_SINGLE([file], [file to consider], )# ARG_POSITIONAL_SINGLE([file], [file to consider], [)# ARG_POSITIONAL_SINGLE([file], [file to consider], [)# ARG_POSITIONAL_SINGLE([file], [file to consider], [-)# ARG_POSITIONAL_SINGLE([file], [file to consider], [-)# ARG_POSITIONAL_SINGLE([file], [file to consider], [-])# ARG_POSITIONAL_SINGLE([file], [file to consider], [-])Save modified buffer? Y Yes N No ^C Cancel Write to File: head.sh ^G Help M-D DOS Format M-A Append M-B Backup File ^C Cancel M-M Mac Format M-P Prepend ^T Browse [matej@matej-mrazak tmp]$ [matej@matej-mrazak tmp]$ . [matej@matej-mrazak tmp]$ ./ [matej@matej-mrazak tmp]$ ./h [matej@matej-mrazak tmp]$ ./he [matej@matej-mrazak tmp]$ ./hed [matej@matej-mrazak tmp]$ ./hea [matej@matej-mrazak tmp]$ ./head.sh [matej@matej-mrazak tmp]$ ./head.sh This is just a script template, not the script (yet) - pass it to 'argbash' to fix this.[matej@matej-mrazak tmp]$ arga [matej@matej-mrazak tmp]$ arg [matej@matej-mrazak tmp]$ argb [matej@matej-mrazak tmp]$ argba [matej@matej-mrazak tmp]$ argbas [matej@matej-mrazak tmp]$ argbash [matej@matej-mrazak tmp]$ argbash [matej@matej-mrazak tmp]$ argbash - [matej@matej-mrazak tmp]$ argbash -i [matej@matej-mrazak tmp]$ argbash -i [matej@matej-mrazak tmp]$ argbash -i h [matej@matej-mrazak tmp]$ argbash -i he [matej@matej-mrazak tmp]$ argbash -i hea [matej@matej-mrazak tmp]$ argbash -i head [matej@matej-mrazak tmp]$ argbash -i head. [matej@matej-mrazak tmp]$ argbash -i head.s [matej@matej-mrazak tmp]$ argbash -i head.sh [matej@matej-mrazak tmp]$ argbash -i head.sh [matej@matej-mrazak tmp]$ . [matej@matej-mrazak tmp]$ ./ [matej@matej-mrazak tmp]$ ./h [matej@matej-mrazak tmp]$ ./he [matej@matej-mrazak tmp]$ ./hea [matej@matej-mrazak tmp]$ ./head [matej@matej-mrazak tmp]$ ./head. [matej@matej-mrazak tmp]$ ./head.s [matej@matej-mrazak tmp]$ ./head.sh [matej@matej-mrazak tmp]$ ./head.shValue of --lines: 10Value of 'file': -[matej@matej-mrazak tmp]$ ./head.sh [matej@matej-mrazak tmp]$ ./head.sh - [matej@matej-mrazak tmp]$ ./head.sh -h [matej@matej-mrazak tmp]$ ./head.sh -h<The general help message of my script>Usage: ./head.sh [-n|--lines <arg>] [-h|--help] [<file>]<file>: file to consider (default: '-')-n, --lines: how many lines (default: '10')-h, --help: Prints help[matej@matej-mrazak tmp]$ ./head.sh - [matej@matej-mrazak tmp]$ ./head.sh -- [matej@matej-mrazak tmp]$ ./head.sh --l [matej@matej-mrazak tmp]$ ./head.sh --li [matej@matej-mrazak tmp]$ ./head.sh --lin [matej@matej-mrazak tmp]$ ./head.sh --line [matej@matej-mrazak tmp]$ ./head.sh --lines [matej@matej-mrazak tmp]$ ./head.sh --lines [matej@matej-mrazak tmp]$ ./head.sh --lines 5 [matej@matej-mrazak tmp]$ ./head.sh --lines 50 [matej@matej-mrazak tmp]$ ./head.sh --lines 50 [matej@matej-mrazak tmp]$ ./head.sh --lines 50 f [matej@matej-mrazak tmp]$ ./head.sh --lines 50 fo [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo Value of --lines: 50Value of 'file': foo[matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo b [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo ba [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo bar [matej@matej-mrazak tmp]$ ./head.sh --lines 50 foo barFATAL ERROR: There were spurious positional arguments --- we expect between 0 and 1, but got 2 (the last one was: 'bar').[matej@matej-mrazak tmp]$ nano head.s #!/bin/bash # Created by argbash-init v2.10.0# ARG_OPTIONAL_SINGLE([lines],[n],[how many lines],[10])# ARG_POSITIONAL_SINGLE([file],[file to consider],[-])# ARG_DEFAULTS_POS() # ARG_HELP([<The general help message of my script>]) # ARGBASH_GO() # needed because of Argbash --> m4_ignore([ ### START OF CODE GENERATED BY Argbash v2.10.0 one line above # [ Read 122 lines ]^G Help ^O Write Out ^F Where Is ^K Cut^T Execute ^C Location#!/bin/bash ### START OF CODE GENERATED BY Argbash v2.10.0 one line above #### Argbash is a bash code generator used to get arguments parsing right. # Argbash is FREE SOFTWARE, see https://argbash.io for more infodie() { local _ret="${2:-1}" test "${_PRINT_HELP:-no}" = yes && print_help >&2echo "$1" >&2 exit "${_ret}" # Created by argbash-init v2.10.0# ARG_OPTIONAL_SINGLE([lines],[n],[how many lines],[10])# ARG_POSITIONAL_SINGLE([file],[file to consider],[-])# ARG_DEFAULTS_POS() # ARG_HELP([<The general help message of my script>]) # ARGBASH_GO() # ## # # A # A# AR # AR# ARG # ARG# ARGB # ARGB# ARGBA # ARGBA# ARGBAS # ARGBAS# ARGBASH # ARGBASH# ARGBASH_ # ARGBASH_# ARGBASH_S # ARGBASH_S# ARGBASH_SE # ARGBASH_SE# ARGBASH_SET # ARGBASH_SET# ARGBASH_SET_ # ARGBASH_SET_# ARGBASH_SET_I # ARGBASH_SET_I# ARGBASH_SET_IN # ARGBASH_SET_IN# ARGBASH_SET_IND # ARGBASH_SET_IND# ARGBASH_SET_INDE # ARGBASH_SET_INDE# ARGBASH_SET_INDEN # ARGBASH_SET_INDEN# ARGBASH_SET_INDENT # ARGBASH_SET_INDENT# ARGBASH_SET_INDENT( # ARGBASH_SET_INDENT(# ARGBASH_SET_INDENT([ # ARGBASH_SET_INDENT([# ARGBASH_SET_INDENT([ # ARGBASH_SET_INDENT([ # ARGBASH_SET_INDENT([ # ARGBASH_SET_INDENT([ # ARGBASH_SET_INDENT([ ] # ARGBASH_SET_INDENT([ ]# ARGBASH_SET_INDENT([ ]) # ARGBASH_SET_INDENT([ ])(reverse-i-search)`': (reverse-i-search)`-': ./head.sh --lines 50 foo bar (reverse-i-search)`-i': argbash -i head.sh [matej@matej-mrazak tmp]$ argbash -i head.sh [matej@matej-mrazak tmp]$ argbash -i head.sh [matej@matej-mrazak tmp]$ argbash -i head.sh [matej@matej-mrazak tmp]$ argbash - -i head.sh [matej@matej-mrazak tmp]$ argbash -- -i head.sh [matej@matej-mrazak tmp]$ argbash --c -i head.sh [matej@matej-mrazak tmp]$ argbash --co -i head.sh [matej@matej-mrazak tmp]$ argbash --com -i head.sh [matej@matej-mrazak tmp]$ argbash --comm -i head.sh [matej@matej-mrazak tmp]$ argbash --comme -i head.sh [matej@matej-mrazak tmp]$ argbash --commen -i head.sh [matej@matej-mrazak tmp]$ argbash --comment -i head.sh [matej@matej-mrazak tmp]$ argbash --commente -i head.sh [matej@matej-mrazak tmp]$ argbash --commented -i head.sh [matej@matej-mrazak tmp]$ argbash --commented -i head.sh #!/bin/bash # Created by argbash-init v2.10.0 # ARG_POSITIONAL_SINGLE([file],[file to consider],[-]) # ARG_DEFAULTS_POS([]) # ARGBASH_SET_INDENT([ ]) # ARGBASH_GO() # needed because of Argbash --> m4_ignore([ ### START OF CODE GENERATED BY Argbash [ Read 166 lines ] #!/bin/bash # Argbash is a bash code generator used to get arguments parsing right.# # When called, the process ends. # Args: # $1: The exit message (print to stderr)# $2: The exit code (default is 1) # if env var _PRINT_HELP is set to 'yes', the usage is print to stderr (prior t> # Created by argbash-init v2.10.0 # ARG_POSITIONAL_SINGLE([file],[file to consider],[-]) # ARG_DEFAULTS_POS([]) # ARG_HELP([<The general help message of my script>])# ARGBASH_SET_INDENT([ ]) # ARGBASH_GO() # needed because of Argbash --> m4_ignore([ ### START OF CODE GENERATED BY Argbash v2.10.0 one line above #### Argbash is a bash code generator used to get arguments parsing right.# Argbash is FREE SOFTWARE, see https://argbash.io for more info # # When called, the process ends. # Args: # $1: The exit message (print to stderr)# $2: The exit code (default is 1) # if env var _PRINT_HELP is set to 'yes', the usage is print to stderr (prior t># Example: # Example: # test -f "$_arg_infile" || _PRINT_HELP=yes die "Can't continue, have to supp># test -f "$_arg_infile" || _PRINT_HELP=yes die "Can't continue, have to supp>die()die(){{ local _ret="${2:-1}"local _ret="${2:-1}" test "${_PRINT_HELP:-no}" = yes && print_help >&2test "${_PRINT_HELP:-no}" = yes && print_help >&2 echo "$1" >&2echo "$1" >&2 exit "${_ret}"exit "${_ret}"}}# Function that evaluates whether a value passed to it begins by a character# Function that evaluates whether a value passed to it begins by a character# that is a short option of an argument the script knows about.# that is a short option of an argument the script knows about.# This is required in order to support getopts-like short options grouping.# This is required in order to support getopts-like short options grouping.begins_with_short_option()begins_with_short_option() local first_option all_short_options='nh'local first_option all_short_options='nh' first_option="${1:0:1}"first_option="${1:0:1}" test "$all_short_options" = "${all_short_options/$first_option/}" && return 1>test "$all_short_options" = "${all_short_options/$first_option/}" && return 1># THE DEFAULTS INITIALIZATION - POSITIONALS# THE DEFAULTS INITIALIZATION - POSITIONALS# The positional args array has to be reset before the parsing, because it may ># The positional args array has to be reset before the parsing, because it may ># - for example if this script is sourced by an argbash-powered script.# - for example if this script is sourced by an argbash-powered script._positionals=()_positionals=()_arg_file="-"_arg_file="-"# THE DEFAULTS INITIALIZATION - OPTIONALS# THE DEFAULTS INITIALIZATION - OPTIONALS_arg_lines="10"_arg_lines="10"# Function that prints general usage of the script.# Function that prints general usage of the script.# This is useful if users asks for it, or if there is an argument parsing error># This is useful if users asks for it, or if there is an argument parsing error># and it makes sense to remind the user how the script is supposed to be called.# and it makes sense to remind the user how the script is supposed to be called.print_help()print_help() printf '%s\n' "<The general help message of my script>"printf '%s\n' "<The general help message of my script>" printf 'Usage: %s [-n|--lines <arg>] [-h|--help] [<file>]\n' "$0"printf 'Usage: %s [-n|--lines <arg>] [-h|--help] [<file>]\n' "$0" printf '\t%s\n' "<file>: file to consider (default: '-')"printf '\t%s\n' "<file>: file to consider (default: '-')" printf '\t%s\n' "-n, --lines: how many lines (default: '10')"printf '\t%s\n' "-n, --lines: how many lines (default: '10')" printf '\t%s\n' "-h, --help: Prints help"printf '\t%s\n' "-h, --help: Prints help"# The parsing of the command-line# The parsing of the command-lineparse_commandline()parse_commandline() _positionals_count=0_positionals_count=0 while test $# -gt 0while test $# -gt 0 dodo _key="$1"_key="$1" case "$_key" incase "$_key" in # We support whitespace as a delimiter between option argument and its va># We support whitespace as a delimiter between option argument and its va> # Therefore, we expect the --lines or -n value.# Therefore, we expect the --lines or -n value. # so we watch for --lines and -n.# so we watch for --lines and -n. # Since we know that we got the long or short option,# Since we know that we got the long or short option, # we just reach out for the next argument to get the value.# we just reach out for the next argument to get the value. -n|--lines)-n|--lines) test $# -lt 2 && die "Missing value for the optional argument '$_key'.">test $# -lt 2 && die "Missing value for the optional argument '$_key'."> _arg_lines="$2"_arg_lines="$2" shiftshift ;;;; # We support the = as a delimiter between option argument and its value.# We support the = as a delimiter between option argument and its value. # Therefore, we expect --lines=value, so we watch for --lines=*# Therefore, we expect --lines=value, so we watch for --lines=* # For whatever we get, we strip '--lines=' using the ${var##--lines=} not># For whatever we get, we strip '--lines=' using the ${var##--lines=} not> # to get the argument value# to get the argument value --lines=*)--lines=*) _arg_lines="${_key##--lines=}"_arg_lines="${_key##--lines=}" # We support getopts-style short arguments grouping,# We support getopts-style short arguments grouping, # so as -n accepts value, we allow it to be appended to it, so we watch f># so as -n accepts value, we allow it to be appended to it, so we watch f> # and we strip the leading -n from the argument string using the ${var##-># and we strip the leading -n from the argument string using the ${var##-> -n*)-n*) _arg_lines="${_key##-n}"_arg_lines="${_key##-n}" # The help argurment doesn't accept a value,# The help argurment doesn't accept a value, # we expect the --help or -h, so we watch for them.# we expect the --help or -h, so we watch for them. -h|--help)-h|--help) print_helpprint_help exit 0exit 0 # We support getopts-style short arguments clustering,# We support getopts-style short arguments clustering, # so as -h doesn't accept value, other short options may be appended to i># so as -h doesn't accept value, other short options may be appended to i> # After stripping the leading -h from the argument, we have to make sure# After stripping the leading -h from the argument, we have to make sure # that the first character that follows coresponds to a short option.# that the first character that follows coresponds to a short option. -h*)-h*) *)*) _last_positional="$1"_last_positional="$1" _positionals+=("$_last_positional")_positionals+=("$_last_positional") _positionals_count=$((_positionals_count + 1))_positionals_count=$((_positionals_count + 1)) esacesac shiftshift donedone# Check that we receive expected amount positional arguments.# Check that we receive expected amount positional arguments.# Return 0 if everything is OK, 1 if we have too little arguments# Return 0 if everything is OK, 1 if we have too little arguments# and 2 if we have too much arguments# and 2 if we have too much argumentshandle_passed_args_count()handle_passed_args_count() test "${_positionals_count}" -le 1 || _PRINT_HELP=yes die "FATAL ERROR: There>test "${_positionals_count}" -le 1 || _PRINT_HELP=yes die "FATAL ERROR: There># Take arguments that we have received, and save them in variables of given nam># Take arguments that we have received, and save them in variables of given nam># The 'eval' command is needed as the name of target variable is saved into ano># The 'eval' command is needed as the name of target variable is saved into ano>[matej@matej-mrazak tmp]$ [matej@matej-mrazak tmp]$ . [matej@matej-mrazak tmp]$ ./ [matej@matej-mrazak tmp]$ ./h [matej@matej-mrazak tmp]$ ./he [matej@matej-mrazak tmp]$ ./hea [matej@matej-mrazak tmp]$ ./heaa [matej@matej-mrazak tmp]$ ./heaad [matej@matej-mrazak tmp]$ ./heaad. [matej@matej-mrazak tmp]$ ./heaad [matej@matej-mrazak tmp]$ ./heaa [matej@matej-mrazak tmp]$ ./head [matej@matej-mrazak tmp]$ ./head. [matej@matej-mrazak tmp]$ ./head.sh f [matej@matej-mrazak tmp]$ ./head.sh -n [matej@matej-mrazak tmp]$ ./head.sh -n [matej@matej-mrazak tmp]$ ./head.sh -n 5 [matej@matej-mrazak tmp]$ ./head.sh -n 50 [matej@matej-mrazak tmp]$ ./head.sh -n 50 [matej@matej-mrazak tmp]$ ./head.sh -n 50 [matej@matej-mrazak tmp]$ ./head.sh -n 50[matej@matej-mrazak tmp]$ ./head.sh -n 50[matej@matej-mrazak tmp]$ ./head.sh -n50 [matej@matej-mrazak tmp]$ ./head.sh -n50 [matej@matej-mrazak tmp]$ ./head.sh -n50 [matej@matej-mrazak tmp]$ ./head.sh -n50[matej@matej-mrazak tmp]$ ./head.sh -n50[matej@matej-mrazak tmp]$ ./head.sh -50 [matej@matej-mrazak tmp]$ ./head.sh --50 [matej@matej-mrazak tmp]$ ./head.sh --l50 [matej@matej-mrazak tmp]$ ./head.sh --li50 [matej@matej-mrazak tmp]$ ./head.sh --lin50 [matej@matej-mrazak tmp]$ ./head.sh --line50 [matej@matej-mrazak tmp]$ ./head.sh --lines50 [matej@matej-mrazak tmp]$ ./head.sh --lines=50 [matej@matej-mrazak tmp]$ ./head.sh --lines=50 [matej@matej-mrazak tmp]$ ./head.sh --lines=50 [matej@matej-mrazak tmp]$ ./head.sh --lines=50[matej@matej-mrazak tmp]$ ./head.sh --lines=50[matej@matej-mrazak tmp]$ ./head.sh -n=50 [matej@matej-mrazak tmp]$ ./head.sh -n=50 Value of --lines: =50[matej@matej-mrazak tmp]$ exit + \ No newline at end of file diff --git a/src/collectors.m4 b/src/collectors.m4 index 5a5cd93..d7d8fff 100644 --- a/src/collectors.m4 +++ b/src/collectors.m4 @@ -73,7 +73,7 @@ dnl Checks that the an argument is a correct short option arg dnl $1: The short option "string" dnl $2: The argument name m4_define([_CHECK_SHORT_OPTION_NAME_IS_OK], [m4_ifnblank([$1], [m4_do( - [m4_bmatch([$1], [^[0-9a-zA-z?]$], , + [m4_bmatch([$1], [^[0-9a-zA-Z?]$], , [_COLLECTOR_FEEDBACK([The value of short option '$1' for argument '--$2' is not valid - it has to be either left blank, or exactly one character.]m4_ifnblank([$1], [[ (Yours has ]m4_len([$1])[ characters).]]))])], [m4_set_contains([_ARGS_SHORT], [$1], [_COLLECTOR_FEEDBACK([The short option '$1' (in definition of '--$2') is already used.])],