This question already has an answer here:
Storing bash script argument with multiple values
(1 answer)
Closed 5 years ago.
I have been researching on using bash scripts to process command-line arguments. I have multiple optional arguments, each of which have one or more operands. An example is:
./script.sh -f file1 file2 -s server1 server2
-f itself is optional, but must be followed by filename; -s is optional, can be used without any operands or operands.
I know I can force putting "" on operands so I only deal with arguments with one operand and can use case $1 $2 shift to process it.
But I am interested in doing so without quotes, just to save some typing for the users.
A rough idea would be read in "$#" as one string, and separate them by space, then locate arguments with -/-- and assign operands following them. Maybe I can use an array to do that?
Any suggestions would be welcome.
Thanks folks for your wonderful suggestions. After spending some more time I resolved to the solution below:
Simply put, I use case and few checks to determine if the argument is an option or not. I use only alter flag variables during argument processing and then use the flags to determine what functions I will perform. In a way that I can have options in different order.
main(){
# flags, 1 is false, 0 is true. it's the damn bash LOCAL_DEPLOY=1
SERVER_DEPLOY=1 DRY_RUN=0
FILES=("${ALLOWEDFILES[#]}");
DEPLOYTARGET=("${ALLOWEDSERVERS[#]}");
if [ $# -eq 0 ]
then
printf -- "Missing optins, perform DRY RUN\nFor help, run with -h/--help\n"
for target in "${FILES[#]}"; do generate "$target"; done
echo "....dry run: markdown files generated in rendered/"
exit 0
fi
while true ; do
case "$1" in
-f |--file) # required operands
case "$2" in
"") die $1 ;;
*)
FILES=($2)
for i in "${FILES[#]}"; do
if is_option $i; then die $1; fi # check for option
if ! check_allowed $i ${ALLOWEDFILES[#]}; then exit 1; fi
done;
shift 2;; # input FILES are good
esac ;;
-l|--local) # no operands expected
DRY_RUN=1 # turn off dryrun
LOCAL_DEPLOY=0 # turn on local deploy
shift ;;
-s|--server) # optional operands
case "$2" in
"") shift ;;
*)
DEPLOYTARGET=($2) # use input
for i in "${DEPLOYTARGET[#]}"; do
if is_option $i; then die $1; fi # check for option
if ! check_allowed $i ${ALLOWEDSERVERS[#]}; then exit 1; fi
done ; shift 2;; # use input value
esac
DRY_RUN=1
SERVER_DEPLOY=0
;;
-n|--dryrun) # dry-run:generate markdown files only
DRY_RUN=0
shift ;;
-h|--help) # docs
print_help
exit 0
;;
--) shift; break ;;
-?*)
printf 'ERROR: Unkown option: %s\nExisting\n\n' "$1" >&2
print_help
exit 1
shift
;;
*)
break ;;
esac
done
echo "choose files: ${FILES[#]}"
echo ""
# dry-run
if [ $DRY_RUN == 0 ]; then
echo "..perform dry run.."
for target in "${FILES[#]}"; do generate "$target"; done
echo "....dry run: markdown files generated in rendered/"
exit 0
fi
# local-deploy
if [ $LOCAL_DEPLOY == 0 ] && [ $SERVER_DEPLOY != 0 ]; then
echo "..deploy locally"
for target in "${FILES[#]}"; do
generate "$target" > /dev/null
deploylocal "$target"
done;
# sync hexo-gcs hexo-yby
cd "$(dirname $HEXOLOCATION)"
./syncRepo.sh
printf -- "....hexo-gcs hexo-yby synced\n"
cd $CURRENTLOCATION
fi
# server-deploy
if [ $SERVER_DEPLOY == 0 ]; then
echo "..deploy on servers: ${DEPLOYTARGET[#]}"
echo ""
for target in "${FILES[#]}"; do # deploy locally
generate "$target" > /dev/null
deploylocal "$target"
done
# sync hexo-gcs hexo-yby
cd "$(dirname $HEXOLOCATION)"
./syncRepo.sh
printf -- "....hexo-gcs hexo-yby synced\n"
cd $CURRENTLOCATION
# deploy to selected server: git or gcp
for dt in "${DEPLOYTARGET[#]}"; do
deployserver $dt
done
fi
}
Related
I am new with bash and after reading and trying a lot about how to parse arguments I cannot what I really want to do I want to parse optional and not optional arguments. More specifically I want to parse 3 arguments, first (a fastaq file) second (a second optional fastaq file) a third argument that will be a directory.
my_script.sh -f1 file1.fasta --f2 file2.fasta -d/home/folder1/folder2
or
my_script.sh -f1 file1.fasta -d /home/folder1/folder2
I have tried to do this in many ways but I dont know how to let the program identifies when there are two fasta files and a directory and, when there is only one fasta file and a directory.
With this arguments I want to save them in variables because they will be used later by third parties.
I have tried this:
for i in "$#"; do
case $i in
-f1=|-fasta1=)
FASTA1="${i#=}"
shift # past argument=value
;;
-d) DIRECTORY=$2
shift 2
;;
-d=|-directory=) DIRECTORY="${i#=}"
shift # past argument=value
;;
--f2=|-fasta2=) FASTA2="${i#*=}"
shift # past argument=value
;;
*)
;;
esac
done
But I just got this
scripts_my_first_NGS]$ ./run.sh -f1 fasta.fasta -d /home/folder1
FASTA1 =
DIRECTORY =
FASTA2 =
Never parse command line options on your own!
Instead either use the Bash function getopts, if you do not need GNU style long options or use use the GNU program getopt otherwise.
The following examples uses an array for FASTA. FASTA1 is ${FASTA[0]} and FASTA2 is ${FASTA[1]}. In case of getopts this makes it possible to use just one option character (-f) multiple times.
Using getopts with only one-character options:
#! /bin/bash
FASTA=()
DIRECTORY=
while getopts 'f:d:' option; do
case "$option" in
f)
FASTA+=("$OPTARG")
;;
d)
DIRECTORY="$OPTARG"
;;
*)
printf 'ERROR: Invalid argument\n' >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [[ -z ${FASTA[0]} ]]; then
printf 'ERROR: FASTA1 missing\n' >&2
exit 1
fi
if [[ -z $DIRECTORY ]]; then
printf 'ERROR: DIRECTORY missing\n' >&2
exit 1
fi
printf 'FASTA1 = %s\n' "${FASTA[0]}"
printf 'FASTA2 = %s\n' "${FASTA[1]}"
printf 'DIRECTORY = %s\n' "$DIRECTORY"
Usage:
run -f file1.fasta -f file2.fasta -d /home/folder1/folder2
Using getopt with one-character and GNU style long options mixed:
#! /bin/bash
FASTA=()
DIRECTORY=
options=$(getopt -o d: -l f1: -l f2: -- "$#") || {
printf 'ERROR: Invalid argument\n' >&2
exit 1
}
eval set -- "$options"
while true; do
case "$1" in
--f1)
FASTA[0]="$2"
shift 2;;
--f2)
FASTA[1]="$2"
shift 2;;
-d)
DIRECTORY="$2"
shift 2;;
--)
shift
break;;
*)
break;;
esac
done
if [[ -z ${FASTA[0]} ]]; then
printf 'ERROR: FASTA1 missing\n' >&2
exit 1
fi
if [[ -z $DIRECTORY ]]; then
printf 'ERROR: DIRECTORY missing\n' >&2
exit 1
fi
printf 'FASTA1 = %s\n' "${FASTA[0]}"
printf 'FASTA2 = %s\n' "${FASTA[1]}"
printf 'DIRECTORY = %s\n' "$DIRECTORY"
Usage:
run --f1 file1.fasta --f2 file2.fasta -d /home/folder1/folder2
Basically you need to add a separate parser for versions of the options where they aren't used with the equal sign.
Also your shift commands are useless since you're processing a for loop. So convert it to to a while [[ $# -gt 0 ]]; do loop instead.
I also added a few modifications which I suggest be added.
while [[ $# -gt 0 ]]; do
case $1 in
-f1|-fasta1)
FASTA1=$2
shift
;;
-f1=*|-fasta1=*)
FASTA1=${1#*=}
;;
-d|-directory)
DIRECTORY=$2
shift
;;
-d=*|-directory=*)
DIRECTORY=${1#*=}
;;
-f2|fasta2)
FASTA2=$2
shift
;;
-f2=*|-fasta2=*)
FASTA2=${1#*=}
;;
-*)
echo "Invalid option: $1" >&2
exit 1
;;
--)
# Do FILES+=("${#:2}") maybe
break
;;
*)
# TODO
# Do FILES+=("$1") maybe
;;
esac
shift
done
The "parser" for the with-equal and non-with-equal versions of the options can also be unified by
using a helper function:
function get_opt_arg {
if [[ $1 == *=* ]]; then
__=${1#*=}
return 1
elif [[ ${2+.} ]]; then
__=$2
return 0 # Tells that shift is needed
else
echo "No argument provided to option '$1'." >&2
exit 1
fi
}
while [[ $# -gt 0 ]]; do
case $1 in
-d|-directory|-d=*|-directory=*)
get_opt_arg "$#" && shift
DIRECTORY=$__
;;
-f1|-fasta1|-f1=*|-fasta1=*)
get_opt_arg "$#" && shift
FASTA1=$__
;;
-f2|fasta2|-f2=*|-fasta2=*)
get_opt_arg "$#" && shift
FASTA2=$__
;;
-*)
echo "Invalid option: $1" >&2
exit 1
;;
--)
# Do FILES+=("${#:2}") maybe
break
;;
*)
# TODO
# Do FILES+=("$1") maybe
;;
esac
shift
done
Update
I found a complete solution to command-line parsing without relying on getopt[s] and it does it even more consistentlty: https://konsolebox.io/blog/2022/05/14/general-command-line-parsing-solution-without-using-getopt-s.html
Here's the code:
#!/bin/bash
function print_help() {
echo "Just a pile of echos; no other logic here"
}
die() {
printf '%s\n' "$1" >&2
exit 1
}
IpAddress=
SourceDir=
DestDir=
backup=0
restore=0
while :; do
case $1 in
''|-h|\?|--help) print_help
exit 0
;;
-b|--backup)
if [ $restore -eq 0 ]; then
backup=1
echo "Taking backup..."
else
die "ERROR: Can't perform backup and restore at the same time."
fi
;;
-r|--restore)
if [ $backup -eq 0 ]; then
restore=1
echo "Restoring backup..."
else
die "ERROR: Can't perform backup and restore at the same time."
fi
;;
--ip)
if [ "$2" ]; then
ip=$2
shift
else
die 'ERROR: "--ip" requires a non-empty option argument.'
fi
;;
--ip=?*)
IpAddress=${1#*=}
echo "IP Address: $IpAddress"
;;
--source-dir=?*)
SourceDir=${1#*=}
if [ -d "$SourceDir" ]; then
echo "Source Directory: $SourceDir indeed exists"
else
die "ERROR: $SourceDir doesn't exist."
fi
;;
--source-dir=)
die 'ERROR: "--source-dir" requires a non-empty argument.'
;;
--dest-dir=?*)
DestDir=${1#*=}
if [ -w "$DestDir" ]; then
echo "Destination Directory: $DestDir indeed exists and is writable."
else
die "ERROR: $DestDir doesn't exist or is not writable for current user."
fi
;;
--dest-dir=)
die 'ERROR: "--dest-dir" requires a non-empty argument.'
;;
--)
shift
break
;;
-?*)
printf 'Warning: Unknown option (ignoring): %s\n' "$1" >&2
;;
*)
break
esac
shift
done
So far, I've tested two use cases, with directories:
/tmp/back/ (in which user has no read permissions) and
/tmp/back2/ (which fully belongs to the user).
The 1st option launched correctly prints the error message:
$ ./backup.sh -b --source-dir=/opt/ --dest-dir=/tmp/back/
Taking backup...
Source Directory: /opt/ indeed exists
ERROR: /tmp/back/ doesn't exist or is not writable for current user.
Meanwhile the 2nd option confirms that data is OK, and seems to come back to the print_help case:
$ ./backup.sh -b --source-dir=/opt/ --dest-dir=/tmp/back2/
Taking backup...
Source Directory: /opt/ indeed exists
Destination Directory: /tmp/back2/ indeed exists and is writable.
backup.sh: backup.sh <-b|r> [-h] [server IP] <source dir> <dest dir>
Take or restore a backup of files.
What should be corrected here in order to exit the script after all options are checked one time?
The problem is that your while loop is an infinite loop, and you only break out of that loop in two of your 12 cases, or when an error occurs. So unless the code encounters those two cases, or an error, it will keep iterating even when there are no more parameters to read. When all of the parameters have been shifted, $1 will evaluate to an empty string, which is explicitly listed in your first case (''|-h|\?|--help) print_help), resulting in the help message.
The solution is to test for the existence of parameters in your while loop:
if [ $# -eq 0 ]; then
print_help
exit 1
fi
while [ $1 -gt 0 ]; do
case $1 in
-h|\?|--help) print_help
...
done
That way, when the last parameter has been shifted, the loop will end.
When all args ended $1 is '' that is why it comes back to the print_help
What should be corrected here in order to exit the script after all
options are checked one time?
Change print_help pattern to *) and '') should be exit 0
But better change while condition to:
while [[ $1 ]]; do
...
done
And this check could be added at the beginning of the script:
[[ $1 ]] || print_help
to print help message and exit if no args passed to the script.
Remove '' from the case statement and test if there are no arguments before the loop:
[[ $1 ]] || { print_help ; exit 0 ; }
while :; do
case $1 in
-h|\?|--help) print_help
exit 0
;;
I'm working on a script that would allow me to add, remove, or edit config files. I have tested it a little and it seems like I got it to work at least with a single file, but I would like to be able to just do .config or fi.config and have it perform the desired action.
I would appreciate any help.
Config file looks looks similar to this just bigger
-- Config File
-- Environment DEV7
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- General properties
-------------------------------------------------------------------------------
com.x.yy.zz.version=2.0.2
com.x.yy.zz.instanceRole.ServerA=PRIMARY
com.x.yy.zz.instanceRole.ServerB=SECONDARY
com.x.yy.zz.StopDelay=30
com.x.yy.zz.sourceType=t
com.x.yy.zz.sNumberInc=20000
com.x.yy.zz.sNumberMin=20000
com.x.yy.zz.sNumberMax=9980000
so -a would allow me to add a line after something
ex. -a StopDealy New
com.x.yy.zz.StopDelay=30
New
#!/bin/bash
i=1
usage()
{
echo "test usage"
}
if [[ $# -gt 4 ]]
then
i=2
fi
while [[ $# -gt $i ]]
do
key="$1"
case $key in
-f|--file)
file="$2"
shift
;;
-a|--after)
sed -i "/$2/a $3" $file
#shift # past argument
;;
-b|--before)
sed -i "/$2/i $3" $file
#shift
;;
-d|--delete)
sed -i "/$2/d" $file
#shift
;;
-e|--edit)
sed -ie "s/$2/$3/g" $file
shift
;;
*)
usage
;;
esac
shift # past argument or value
done
I didn't test it yet, but this is the closest version to what I understand you want to achieve.
#!/bin/bash
usage() {
echo "Usage: $0 -f file1 -f *.txt -[abde] ... -f file3 ... -[abde] ... "
}
# Do all the required action on one file
do_work() {
file="$1" # 1st argument must be the file to work on
shift
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file) while [[ ! "$2" = -* && $# -gt 0 ]]; do shift; done ;; # Ignore any "file" since we have one to work on.
-a|--after) sed -i "/$2/a $3" $file; shift 2 ;;
-b|--before) sed -i "/$2/i $3" $file; shift 2 ;;
-d|--delete) sed -i "/$2/d" $file; shift ;;
-e|--edit) sed -ie "s/$2/$3/g" $file; shift 2 ;;
esac
shift # past argument or value
done
}
# Check the arguments for files and print the valid ones
# Other arguments will just be skipped
# Invalid arguments will be displayed.
identify_files() {
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file) # Validate the the file exists (just in case)
# check each following argument until next option
# ... or end of arguments
while [[ ! "$2" = -* && $# -gt 0 ]]; do
if [[ -f "$2" ]]; then
echo "$2"
else
echo "Error: Invalid file '$2'." >&2
fi
shift
done ;;
-[abe]) shift 2 ;;
-d) shift ;;
-h) usage >&2; exit ;;
*) echo "Invalid otpion '$1'" >&2 ;;
esac
shift
done
}
# Do the required actions on all files received in the options
for File in $(identify_files "$#"); do
do_work "$File" "$#"
done
## Alternative on predefined files (not the ones received as arguments)
## Usage: $0 -[abde] ...
#for File in $(ls *.config); do
# do_work "$File" "$#"
#done
I'm trying to create a script which will have a flag with optional options. With getopts it's possible to specify a mandatory argument (using a colon) after the flag, but I want to keep it optional.
It will be something like this:
./install.sh -a 3
or
./install.sh -a3
where 'a' is the flag and '3' is the optional parameter that follows a.
Thanks in advance.
The getopt external program allows options to have a single optional argument by adding a double-colon to the option name.
# Based on a longer example in getopt-parse.bash, included with
# getopt
TEMP=$(getopt -o a:: -- "$#")
eval set -- "$TEMP"
while true ; do
case "$1" in
-a)
case "$2" in
"") echo "Option a, no argument"; shift 2 ;;
*) echo "Option a, argument $2"; shift 2;;
esac ;;
--) shift; break ;;
*) echo "Internal error!"; exit 1 ;;
esac
done
The following is without getopt and it takes an optional argument with the -a flag:
for WORD; do
case $WORD in
-a?) echo "single arg Option"
SEP=${WORD:2:1}
echo $SEP
shift ;;
-a) echo "split arg Option"
if [[ ${2:0:1} != "-" && ${2:0:1} != ""]] ; then
SEP=$2
shift 2
echo "arg present"
echo $SEP
else
echo "optional arg omitted"
fi ;;
-a*) echo "arg Option"
SEP=${WORD:2}
echo $SEP
shift ;;
-*) echo "Unrecognized Short Option"
echo "Unrecognized argument"
;;
esac
done
Other options/flags also can be added easily.
Use the getopt feature. On most systems, man getopt will yield documentation for it, and even examples of using it in a script. From the man page on my system:
The following code fragment shows how one might process the arguments
for a command that can take the options -a and -b, and the option -o,
which requires an argument.
args=`getopt abo: $*`
# you should not use `getopt abo: "$#"` since that would parse
# the arguments differently from what the set command below does.
if [ $? != 0 ]
then
echo 'Usage: ...'
exit 2
fi
set -- $args
# You cannot use the set command with a backquoted getopt directly,
# since the exit code from getopt would be shadowed by those of set,
# which is zero by definition.
for i
do
case "$i"
in
-a|-b)
echo flag $i set; sflags="${i#-}$sflags";
shift;;
-o)
echo oarg is "'"$2"'"; oarg="$2"; shift;
shift;;
--)
shift; break;;
esac
done
echo single-char flags: "'"$sflags"'"
echo oarg is "'"$oarg"'"
This code will accept any of the following as equivalent:
cmd -aoarg file file
cmd -a -o arg file file
cmd -oarg -a file file
cmd -a -oarg -- file file
In bash there is some implicit variable:
$#: contains number of arguments for a called script/function
$0: contains names of script/function
$1: contains first argument
$2: contains second argument
...
$n: contains n-th argument
For example:
#!/bin/ksh
if [ $# -ne 2 ]
then
echo "Wrong number of argument - expected 2 : $#"
else
echo "Argument list:"
echo "\t$0"
echo "\t$1"
echo "\t$2"
fi
My solution:
#!/bin/bash
count=0
skip=0
flag="no flag"
list=($#) #put args in array
for arg in $# ; do #iterate over array
count=$(($count+1)) #update counter
if [ $skip -eq 1 ]; then #check if we have to skip this args
skip=0
continue
fi
opt=${arg:0:2} #get only first 2 chars as option
if [ $opt == "-a" ]; then #check if option equals "-a"
if [ $opt == $arg ] ; then #check if this is only the option or has a flag
if [ ${list[$count]:0:1} != "-" ]; then #check if next arg is an option
skip=1 #skip next arg
flag=${list[$count]} #use next arg as flag
fi
else
flag=${arg:2} #use chars after "-a" as flag
fi
fi
done
echo $flag
After several days of research, I still can't figure out the best method for parsing cmdline args in a .sh script. According to my references the getopts cmd is the way to go since it "extracts and checks switches without disturbing the positional parameter variables.Unexpected switches, or switches that are missing arguments, are recognized and reportedas errors."
Positional params(Ex. 2 - $#, $#, etc) apparently don't work well when spaces are involved but can recognize regular and long parameters(-p and --longparam). I noticed that both methods fail when passing parameters with nested quotes ("this is an Ex. of ""quotes""."). Which one of these three code samples best illustrates the way to deal with cmdline args? The getopt function is not recommended by gurus, so I'm trying to avoid it!
Example 1:
#!/bin/bash
for i in "$#"
do
case $i in
-p=*|--prefix=*)
PREFIX=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
;;
-s=*|--searchpath=*)
SEARCHPATH=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
;;
-l=*|--lib=*)
DIR=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
;;
--default)
DEFAULT=YES
;;
*)
# unknown option
;;
esac
done
exit 0
Example 2:
#!/bin/bash
echo ‘number of arguments’
echo "\$#: $#"
echo ”
echo ‘using $num’
echo "\$0: $0"
if [ $# -ge 1 ];then echo "\$1: $1"; fi
if [ $# -ge 2 ];then echo "\$2: $2"; fi
if [ $# -ge 3 ];then echo "\$3: $3"; fi
if [ $# -ge 4 ];then echo "\$4: $4"; fi
if [ $# -ge 5 ];then echo "\$5: $5"; fi
echo ”
echo ‘using $#’
let i=1
for x in $#; do
echo "$i: $x"
let i=$i+1
done
echo ”
echo ‘using $*’
let i=1
for x in $*; do
echo "$i: $x"
let i=$i+1
done
echo ”
let i=1
echo ‘using shift’
while [ $# -gt 0 ]
do
echo "$i: $1"
let i=$i+1
shift
done
[/bash]
output:
bash> commandLineArguments.bash
number of arguments
$#: 0
using $num
$0: ./commandLineArguments.bash
using $#
using $*
using shift
#bash> commandLineArguments.bash "abc def" g h i j*
Example 3:
#!/bin/bash
while getopts ":a:" opt; do
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
exit 0
I find the use of getopt to be the easiest. It provides correct handling of arguments which is tricky otherwise. For example, getopt will know how to handle arguments to a long option specified on the command line as --arg=option or --arg option.
What is useful in parsing any input passed to a shell script is the use of the "$#" variables. See the bash man page for how this differs from $#. It ensures that you can process arguments that include spaces.
Here's an example of how I might write s script to parse some simple command line arguments:
#!/bin/bash
args=$(getopt -l "searchpath:" -o "s:h" -- "$#")
eval set -- "$args"
while [ $# -ge 1 ]; do
case "$1" in
--)
# No more options left.
shift
break
;;
-s|--searchpath)
searchpath="$2"
shift
;;
-h)
echo "Display some help"
exit 0
;;
esac
shift
done
echo "searchpath: $searchpath"
echo "remaining args: $*"
And used like this to show that spaces and quotes are preserved:
user#machine:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"."
searchpath: File with spaces and "quotes".
remaining args: other args
Some basic information about the use of getopt can be found here
If you want to avoid using getopt you can use this nice quick approach:
Defining help with all options as ## comments (customise as you wish).
Define for each option a function with same name.
Copy the last five lines of this script to your script (the magic).
Example script: log.sh
#!/bin/sh
## $PROG 1.0 - Print logs [2017-10-01]
## Compatible with bash and dash/POSIX
##
## Usage: $PROG [OPTION...] [COMMAND]...
## Options:
## -i, --log-info Set log level to info (default)
## -q, --log-quiet Set log level to quiet
## -l, --log MESSAGE Log a message
## Commands:
## -h, --help Displays this help and exists
## -v, --version Displays output version and exists
## Examples:
## $PROG -i myscrip-simple.sh > myscript-full.sh
## $PROG -r myscrip-full.sh > myscript-simple.sh
PROG=${0##*/}
LOG=info
die() { echo $# >&2; exit 2; }
log_info() {
LOG=info
}
log_quiet() {
LOG=quiet
}
log() {
[ $LOG = info ] && echo "$1"; return 1 ## number of args used
}
help() {
grep "^##" "$0" | sed -e "s/^...//" -e "s/\$PROG/$PROG/g"; exit 0
}
version() {
help | head -1
}
[ $# = 0 ] && help
while [ $# -gt 0 ]; do
CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" log.sh | sed -e "s/-/_/g")
if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi
shift; eval "$CMD" $# || shift $? 2> /dev/null
done
Testing
Running this command:
./log.sh --log yep --log-quiet -l nop -i -l yes
Produces this output:
yep
yes
By the way: It's compatible with posix!