Jak przetwarzać argumenty wiersza poleceń w Bash?
Powiedzmy, że mam skrypt, który zostanie wywołany z tej linii:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
Lub ten:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
Jaki jest akceptowany sposób parsowania tego tak, że w każdym przypadku (lub jakaś kombinacja tych dwóch) $v
, $f
, i $d
czy wszystkie będą ustawione na true
i $outFile
będą równe /fizz/someOtherFile
?
29 answers
Metoda #1: Używanie Basha bez getopt [s]
Dwa popularne sposoby przekazywania argumentów klucz-wartość-para to:
Bash oddzielony spacjami (np. --option argument
) (bez getopt[s])
Użycie ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts
#!/bin/bash
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-e|--extension)
EXTENSION="$2"
shift # past argument
shift # past value
;;
-s|--searchpath)
SEARCHPATH="$2"
shift # past argument
shift # past value
;;
-l|--lib)
LIBPATH="$2"
shift # past argument
shift # past value
;;
--default)
DEFAULT=YES
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
echo FILE EXTENSION = "${EXTENSION}"
echo SEARCH PATH = "${SEARCHPATH}"
echo LIBRARY PATH = "${LIBPATH}"
echo DEFAULT = "${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
echo "Last line of file specified as non-opt/last argument:"
tail -1 "$1"
fi
Bash równa się-Oddzielone (np. --option=argument
) (bez getopt [s])
Użycie ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
#!/bin/bash
for i in "$@"
do
case $i in
-e=*|--extension=*)
EXTENSION="${i#*=}"
shift # past argument=value
;;
-s=*|--searchpath=*)
SEARCHPATH="${i#*=}"
shift # past argument=value
;;
-l=*|--lib=*)
LIBPATH="${i#*=}"
shift # past argument=value
;;
--default)
DEFAULT=YES
shift # past argument with no value
;;
*)
# unknown option
;;
esac
done
echo "FILE EXTENSION = ${EXTENSION}"
echo "SEARCH PATH = ${SEARCHPATH}"
echo "LIBRARY PATH = ${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
echo "Last line of file specified as non-opt/last argument:"
tail -1 $1
fi
Aby lepiej zrozumieć ${i#*=}
wyszukaj "usuwanie Podłańcuchów" w w tym przewodniku. Jest funkcjonalnie równoważna `sed 's/[^=]*=//' <<< "$i"`
, która wywołuje zbędny podproces lub `echo "$i" | sed 's/[^=]*=//'`
który wywołuje dwa niepotrzebne podprocesy.
Metoda #2: Używanie bash z getopt [s]
From: http://mywiki.wooledge.org/BashFAQ/035#getopts
Getopt (1) ograniczenia (starsze, stosunkowo najnowsze wersje getopt
):
- nie można obsługiwać argumentów, które są pustymi łańcuchami
- nie można obsługiwać argumentów z osadzonymi białymi znakami
Nowsze getopt
wersje nie mają takich ograniczenia.
Dodatkowo powłoka POSIX (i inne) oferuje getopts
, która nie ma tych ograniczeń. Oto uproszczony getopts
przykład:
#!/bin/sh
# A POSIX variable
OPTIND=1 # Reset in case getopts has been used previously in the shell.
# Initialize our own variables:
output_file=""
verbose=0
while getopts "h?vf:" opt; do
case "$opt" in
h|\?)
show_help
exit 0
;;
v) verbose=1
;;
f) output_file=$OPTARG
;;
esac
done
shift $((OPTIND-1))
[ "${1:-}" = "--" ] && shift
echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
# End of file
Zalety getopts
to:
- jest bardziej przenośny i będzie działał w innych powłokach, takich jak
dash
. - może obsługiwać wiele pojedynczych opcji, takich jak
-vf filename
w typowy uniksowy sposób, automatycznie.
Wadą getopts
jest to, że może obsługiwać tylko krótkie opcje (-h
, a nie --help
) bez dodatkowego kodu.
Istnieje getopts tutorial , który wyjaśnia, co oznaczają wszystkie składnie i zmienne. W bash istnieje również help getopts
, które mogą być pouczające.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-18 07:27:42
Żadna odpowiedź nie wspomina enhanced getopt. A najczęściej głosowana odpowiedź {[20] } jest myląca: ignoruje -vfd
opcje krótkie w stylu (wymagane przez OP), opcje po argumentach pozycyjnych (również wymagane przez OP) i ignoruje błędy parsowania. Zamiast:
-
użyj enhanced
getopt
z util-linux lub wcześniej GNU glibc .1 - Działa z
getopt_long()
funkcją C GNU glibc. - ma wszystkie przydatne cechy wyróżniające (inni ich nie mają):
- obsługuje spacje, cytowanie znaków, a nawet binarne w argumentach2
- może obsługiwać opcje na końcu:
script.sh -o outFile file1 file2 -v
- pozwala
=
- style długie opcje:script.sh --outfile=fileOut --infile fileIn
- jest już taka stara3 że żadnemu systemowi GNU tego nie brakuje (np. każdemu Linuksowi).
- można sprawdzić jego istnienie za pomocą:
getopt --test
→ wartość zwracana 4. - Inne
getopt
lub shell-builtingetopts
mają ograniczone zastosowanie.
Następujące wywołania
myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile
All return
verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile
Z następującym myscript
#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset
! getopt --test > /dev/null
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
echo "I’m sorry, `getopt --test` failed in this environment."
exit 1
fi
OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose
# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via -- "$@" to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
# e.g. return value is 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"
d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
case "$1" in
-d|--debug)
d=y
shift
;;
-f|--force)
f=y
shift
;;
-v|--verbose)
v=y
shift
;;
-o|--output)
outFile="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "Programming error"
exit 3
;;
esac
done
# handle non-option arguments
if [[ $# -ne 1 ]]; then
echo "$0: A single input file is required."
exit 4
fi
echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"
1 enhanced getopt jest dostępny na większości "bash-systems", w tym Cygwin; na OS X spróbuj brew zainstalować gnu-getopt lub sudo port install getopt
2 konwencje POSIX exec()
nie mają niezawodnego sposobu przekazywania binarnego NULL w argumentach wiersza poleceń; te bajty przedwcześnie kończą argument
3 pierwsza wersja wydana w 1997 lub wcześniej (śledziłem ją tylko do 1997)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 18:10:30
Od : digitalpeer.com z niewielkimi modyfikacjami
Użycie myscript.sh -p=my_prefix -s=dirname -l=libname
#!/bin/bash
for i in "$@"
do
case $i in
-p=*|--prefix=*)
PREFIX="${i#*=}"
;;
-s=*|--searchpath=*)
SEARCHPATH="${i#*=}"
;;
-l=*|--lib=*)
DIR="${i#*=}"
;;
--default)
DEFAULT=YES
;;
*)
# unknown option
;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}
Aby lepiej zrozumieć ${i#*=}
wyszukaj "usuwanie Podłańcuchów" w w tym przewodniku. Jest to funkcjonalnie równoważne `sed 's/[^=]*=//' <<< "$i"`
, które wywołuje zbędny podproces lub `echo "$i" | sed 's/[^=]*=//'`
, które wywołuje dwa zbędne podprocesy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-20 18:35:46
getopt()
/getopts()
to dobra opcja. Skradzione z tutaj :
Proste użycie "getopt" jest pokazane w tym mini-skrypcie:
#!/bin/bash
echo "Before getopt"
for i
do
echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
echo "-->$i"
done
Powiedzieliśmy, że każdy z, -b, - c lub-d będą dozwolone, ale po-c następuje argument ("c:" mówi, że).
Jeśli nazwiemy to "g" i wypróbujemy:
bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--
Zaczynamy od dwóch argumentów i "getopt" rozdziela opcje i każdy ma swój własny argument. Informatyka również dodano "--".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 17:03:38
Ryzykując dodanie kolejnego przykładu do zignorowania, oto mój plan.
- uchwyty
-n arg
i--name=arg
- pozwala na argumenty na końcu
- pokazuje poprawne błędy, jeśli coś jest źle napisane
- kompatybilny, nie używa bashismów
- czytelny, nie wymaga utrzymywania stanu w pętli
while [ "$#" -gt 0 ]; do
case "$1" in
-n) name="$2"; shift 2;;
-p) pidfile="$2"; shift 2;;
-l) logfile="$2"; shift 2;;
--name=*) name="${1#*=}"; shift 1;;
--pidfile=*) pidfile="${1#*=}"; shift 1;;
--logfile=*) logfile="${1#*=}"; shift 1;;
--name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;
-*) echo "unknown option: $1" >&2; exit 1;;
*) handle_argument "$1"; shift 1;;
esac
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-21 15:05:05
Bardziej zwięzły sposób
Script.sh
#!/bin/bash
while [[ "$#" > 0 ]]; do case $1 in
-d|--deploy) deploy="$2"; shift;;
-u|--uglify) uglify=1;;
*) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done
echo "Should deploy? $deploy"
echo "Should uglify? $uglify"
Użycie:
./script.sh -d dev -u
# OR:
./script.sh --deploy dev --uglify
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-07 21:13:24
Jestem jakieś 4 lata spóźniony na to pytanie, ale chcę się odwdzięczyć. Wykorzystałem wcześniejsze odpowiedzi jako punkt wyjścia do uporządkowania mojego starego parsowania adhoc param. Następnie refakturowałem poniższy kod szablonu. Obsługuje zarówno długie, jak i krótkie paramy, używając argumentów oddzielonych znakiem = lub spacją, a także wiele krótkich param zgrupowanych razem. W końcu ponownie wstawia wszelkie argumenty nie-param z powrotem do $1,$2.. zmienne. Mam nadzieję, że się przyda.
#!/usr/bin/env bash
# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi
echo "Before"
for i ; do echo - $i ; done
# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.
while [ -n "$1" ]; do
# Copy so we can modify it (can't modify $1)
OPT="$1"
# Detect argument termination
if [ x"$OPT" = x"--" ]; then
shift
for OPT ; do
REMAINS="$REMAINS \"$OPT\""
done
break
fi
# Parse current opt
while [ x"$OPT" != x"-" ] ; do
case "$OPT" in
# Handle --flag=value opts like this
-c=* | --config=* )
CONFIGFILE="${OPT#*=}"
shift
;;
# and --flag value opts like this
-c* | --config )
CONFIGFILE="$2"
shift
;;
-f* | --force )
FORCE=true
;;
-r* | --retry )
RETRY=true
;;
# Anything unknown is recorded for later
* )
REMAINS="$REMAINS \"$OPT\""
break
;;
esac
# Check for multiple short options
# NOTICE: be sure to update this pattern to match valid options
NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
if [ x"$OPT" != x"$NEXTOPT" ] ; then
OPT="-$NEXTOPT" # multiple short opts, keep going
else
break # long form, exit inner loop
fi
done
# Done with that param. move to next
shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS
echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-01 01:20:22
Moja odpowiedź opiera się w dużej mierze naodpowiedzi Bruno Bronosky ' ego , ale w pewnym sensie zmiażdżyłem jego dwie czyste implementacje Basha w jedną, której używam dość często.
# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
key="$1"
case "$key" in
# This is a flag type option. Will catch either -f or --foo
-f|--foo)
FOO=1
;;
# Also a flag type option. Will catch either -b or --bar
-b|--bar)
BAR=1
;;
# This is an arg value type option. Will catch -o value or --output-file value
-o|--output-file)
shift # past the key and to the value
OUTPUTFILE="$1"
;;
# This is an arg=value type option. Will catch -o=value or --output-file=value
-o=*|--output-file=*)
# No need to shift here since the value is part of the same string
OUTPUTFILE="${key#*=}"
;;
*)
# Do whatever you want with extra options
echo "Unknown option '$key'"
;;
esac
# Shift after checking all the cases to get the next option
shift
done
Pozwala to mieć zarówno opcje/wartości rozdzielone spacjami, jak i równe zdefiniowane wartości.
Więc można uruchomić skrypt używając:
./myscript --foo -b -o /fizz/file.txt
Oraz:
./myscript -f --bar -o=/fizz/file.txt
I oba powinny mieć ten sam efekt końcowy.
PLUSY:
-
Pozwala zarówno na-arg = value, jak i-arg wartość
-
Działa z dowolną nazwą arg, której możesz użyć w bash
- znaczenie-a lub-arg lub --arg lub-A-r - g lub cokolwiek
-
Czysty bash. Nie musisz uczyć się / używać getopt lub getopts
Wady:
-
Nie można połączyć args
- czyli nie-abc. Musisz zrobić-A-b-c
To jedyne plusy/minusy, które mogę wymyślić z głowy
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:33
Znalazłem sprawę pisania przenośnego parsowania w skryptach tak frustrujące, że napisałem Argbash - generator kodu FOSS, który może generować argumenty-parsowanie kodu dla Twojego skryptu plus ma kilka fajnych funkcji:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-09 10:39:37
Myślę, że ten jest wystarczająco prosty w użyciu:
#!/bin/bash
#
readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'
opts=vfdo:
# Enumerating options
while eval $readopt
do
echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done
# Enumerating arguments
for arg
do
echo ARG:$arg
done
Przykład wywołania:
./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v
OPT:d
OPT:o OPTARG:/fizz/someOtherFile
OPT:f
ARG:./foo/bar/someFile
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-01 15:15:03
Rozszerzając doskonałą odpowiedź @guneysus, oto tweak, który pozwala użytkownikowi korzystać z dowolnej składni, którą preferuje, np
command -x=myfilename.ext --another_switch
Vs
command -x myfilename.ext --another_switch
Oznacza to, że równe mogą być zastąpione białymi spacjami.
Ta "rozmyta interpretacja" może Ci się nie podobać, ale jeśli tworzysz skrypty, które są wymienne z innymi narzędziami (jak w przypadku mojego, który musi działać z ffmpeg), użyteczna jest elastyczność.
STD_IN=0
prefix=""
key=""
value=""
for keyValue in "$@"
do
case "${prefix}${keyValue}" in
-i=*|--input_filename=*) key="-i"; value="${keyValue#*=}";;
-ss=*|--seek_from=*) key="-ss"; value="${keyValue#*=}";;
-t=*|--play_seconds=*) key="-t"; value="${keyValue#*=}";;
-|--stdin) key="-"; value=1;;
*) value=$keyValue;;
esac
case $key in
-i) MOVIE=$(resolveMovie "${value}"); prefix=""; key="";;
-ss) SEEK_FROM="${value}"; prefix=""; key="";;
-t) PLAY_SECONDS="${value}"; prefix=""; key="";;
-) STD_IN=${value}; prefix=""; key="";;
*) prefix="${keyValue}=";;
esac
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-09 14:07:58
Getopts działa świetnie, jeśli #1 masz go zainstalowany i # 2 zamierzasz uruchomić go na tej samej platformie. OSX i Linux (na przykład) zachowują się inaczej pod tym względem.
Oto rozwiązanie (non getopts), które obsługuje znaczniki equals, non-equals i boolean. Na przykład możesz uruchomić swój skrypt w ten sposób:
./script --arg1=value1 --arg2 value2 --shouldClean
# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
arg=${ARGS[$COUNTER]}
let COUNTER=COUNTER+1
nextArg=${ARGS[$COUNTER]}
if [[ $skipNext -eq 1 ]]; then
echo "Skipping"
skipNext=0
continue
fi
argKey=""
argVal=""
if [[ "$arg" =~ ^\- ]]; then
# if the format is: -key=value
if [[ "$arg" =~ \= ]]; then
argVal=$(echo "$arg" | cut -d'=' -f2)
argKey=$(echo "$arg" | cut -d'=' -f1)
skipNext=0
# if the format is: -key value
elif [[ ! "$nextArg" =~ ^\- ]]; then
argKey="$arg"
argVal="$nextArg"
skipNext=1
# if the format is: -key (a boolean flag)
elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
argKey="$arg"
argVal=""
skipNext=0
fi
# if the format has not flag, just a value.
else
argKey=""
argVal="$arg"
skipNext=0
fi
case "$argKey" in
--source-scmurl)
SOURCE_URL="$argVal"
;;
--dest-scmurl)
DEST_URL="$argVal"
;;
--version-num)
VERSION_NUM="$argVal"
;;
-c|--clean)
CLEAN_BEFORE_START="1"
;;
-h|--help|-help|--h)
showUsage
exit
;;
esac
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-12 22:17:48
Podaję Ci funkcję parse_params
, która będzie analizować params z linii poleceń.
- jest to czyste rozwiązanie Bash, bez dodatkowych narzędzi.
- nie zanieczyszcza globalnego zasięgu.
- bez wysiłku zwraca proste w użyciu zmienne, na których można zbudować dalszą logikę.
- Ilość kresek przed params nie ma znaczenia (
--all
równa się-all
równa sięall=all
)
Poniższy skrypt to demonstracja pracy kopiuj-wklej. Patrz show_use
Funkcja do zrozumieć, jak używać parse_params
.
Ograniczenia:
- nie obsługuje param rozdzielonych spacjami(
-d 1
) - nazwy Param utracą myślniki, więc
--any-param
i-anyparam
są równoważne -
eval $(parse_params "$@")
musi być użyta wewnątrz funkcji bash (nie będzie działać w zakresie globalnym)
#!/bin/bash
# Universal Bash parameter parsing
# Parses equal sign separated params into local variables (--name=bob creates variable $name=="bob")
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Parses un-named params into ${ARGV[*]} array
# Additionally puts all named params raw into ${ARGN[*]} array
# Additionally puts all standalone "option" params raw into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4 (Jun-26-2018)
parse_params ()
{
local existing_named
local ARGV=() # un-named params
local ARGN=() # named params
local ARGO=() # options (--params)
echo "local ARGV=(); local ARGN=(); local ARGO=();"
while [[ "$1" != "" ]]; do
# Escape asterisk to prevent bash asterisk expansion
_escaped=${1/\*/\'\"*\"\'}
# If equals delimited named parameter
if [[ "$1" =~ ^..*=..* ]]; then
# Add to named parameters array
echo "ARGN+=('$_escaped');"
# key is part before first =
local _key=$(echo "$1" | cut -d = -f 1)
# val is everything after key and = (protect from param==value error)
local _val="${1/$_key=}"
# remove dashes from key name
_key=${_key//\-}
# skip when key is empty
if [[ "$_key" == "" ]]; then
shift
continue
fi
# search for existing parameter name
if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
# if name already exists then it's a multi-value named parameter
# re-declare it as an array if needed
if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
echo "$_key=(\"\$$_key\");"
fi
# append new value
echo "$_key+=('$_val');"
else
# single-value named parameter
echo "local $_key=\"$_val\";"
existing_named=" $_key"
fi
# If standalone named parameter
elif [[ "$1" =~ ^\-. ]]; then
# remove dashes
local _key=${1//\-}
# skip when key is empty
if [[ "$_key" == "" ]]; then
shift
continue
fi
# Add to options array
echo "ARGO+=('$_escaped');"
echo "local $_key=\"$_key\";"
# non-named parameter
else
# Escape asterisk to prevent bash asterisk expansion
_escaped=${1/\*/\'\"*\"\'}
echo "ARGV+=('$_escaped');"
fi
shift
done
}
#--------------------------- DEMO OF THE USAGE -------------------------------
show_use ()
{
eval $(parse_params "$@")
# --
echo "${ARGV[0]}" # print first unnamed param
echo "${ARGV[1]}" # print second unnamed param
echo "${ARGN[0]}" # print first named param
echo "${ARG0[0]}" # print first option param (--force)
echo "$anyparam" # print --anyparam value
echo "$k" # print k=5 value
echo "${multivalue[0]}" # print first value of multi-value
echo "${multivalue[1]}" # print second value of multi-value
[[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}
show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-06 13:00:40
Tak robię w funkcji, aby uniknąć łamania getoptów uruchamianych w tym samym czasie gdzieś wyżej w stosie:
function waitForWeb () {
local OPTIND=1 OPTARG OPTION
local host=localhost port=8080 proto=http
while getopts "h:p:r:" OPTION; do
case "$OPTION" in
h)
host="$OPTARG"
;;
p)
port="$OPTARG"
;;
r)
proto="$OPTARG"
;;
esac
done
...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-19 07:50:31
EasyOptions nie wymaga żadnego parsowania:
## Options:
## --verbose, -v Verbose mode
## --output=FILE Output filename
source easyoptions || exit
if test -n "${verbose}"; then
echo "output file is ${output}"
echo "${arguments[@]}"
fi
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-04 16:47:40
Chciałbym zaoferować moją wersję parsowania opcji, która pozwala na:
-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello
Pozwala również na to (może być niechciane):
-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder
Musisz zdecydować przed użyciem, czy = ma być używany w danej opcji, czy nie. Ma to na celu zachowanie czystości kodu (ish).
while [[ $# > 0 ]]
do
key="$1"
while [[ ${key+x} ]]
do
case $key in
-s*|--stage)
STAGE="$2"
shift # option has parameter
;;
-w*|--workfolder)
workfolder="$2"
shift # option has parameter
;;
-e=*)
EXAMPLE="${key#*=}"
break # option has been fully handled
;;
*)
# unknown option
echo Unknown option: $key #1>&2
exit 10 # either this: my preferred way to handle unknown options
break # or this: do this to signal the option has been handled (if exit isn't used)
;;
esac
# prepare for next option in this key, if any
[[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
done
shift # option(s) fully processed, proceed to next input argument
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 10:54:23
Zauważ, że getopt(1)
był krótkim życiowym błędem AT & T.
Getopt został stworzony w 1984 roku, ale już pogrzebany w 1986 roku, ponieważ nie był naprawdę użyteczny.
Dowodem na to, że getopt
jest bardzo nieaktualna jest to, że strona podręcznika getopt(1)
nadal wspomina "$*"
zamiast "$@"
, która została dodana do powłoki Bourne ' a w 1986 roku wraz z powłoką getopts(1)
wbudowaną w celu radzenia sobie z argumentami ze spacjami wewnątrz.
BTW: jeśli jesteś zainteresowany analizowaniem długich opcji w powłoce skryptów, warto wiedzieć, że implementacja getopt(3)
z libc (Solaris) i ksh93
dodała jednolitą implementację długich opcji, która obsługuje długie opcje jako aliasy dla krótkich opcji. Powoduje to, że ksh93
i Bourne Shell
implementują jednolity interfejs dla długich opcji poprzez getopts
.
Przykład długich opcji pobranych ze strony podręcznika powłoki Bourne ' a:
getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"
Pokazuje jak długie aliasy opcji mogą być używane zarówno w powłoce Bourne ' a jak i ksh93
Zobacz stronę podręcznika najnowszej powłoki Bourne ' a:
Http://schillix.sourceforge.net/man/man1/bosh.1.html
I strona podręcznika dla getopt (3) z OpenSolaris:
Http://schillix.sourceforge.net/man/man3c/getopt.3c.html
I Ostatnia, strona podręcznika getopt (1), Aby zweryfikować nieaktualne $*:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-20 21:59:55
Mieszanie argumentów pozycyjnych i flagowych
--param = arg (equals delimited)
Dowolne mieszanie FLAG między pozycyjnymi argumentami:
./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d
Można osiągnąć dość zwięzłym podejściem:
# process flags
pointer=1
while [[ $pointer -le $# ]]; do
param=${!pointer}
if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
else
case $param in
# paramter-flags with arguments
-e=*|--environment=*) environment="${param#*=}";;
--another=*) another="${param#*=}";;
# binary flags
-q|--quiet) quiet=true;;
-d) debug=true;;
esac
# splice out pointer frame from positional list
[[ $pointer -gt 1 ]] \
&& set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
|| set -- ${@:((pointer + 1)):$#};
fi
done
# positional remain
node_name=$1
ip_address=$2
-- param arg (spacja rozdzielona)
Jest zwykle jaśniejsze, aby nie mieszać --flag=value
i --flag value
stylów.
./script.sh dumbo 127.0.0.1 --environment production -q -d
To jest trochę ryzykowne do czytania, ale nadal jest ważne
./script.sh dumbo --environment production 127.0.0.1 --quiet -d
Źródło
# process flags
pointer=1
while [[ $pointer -le $# ]]; do
if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
else
param=${!pointer}
((pointer_plus = pointer + 1))
slice_len=1
case $param in
# paramter-flags with arguments
-e|--environment) environment=${!pointer_plus}; ((slice_len++));;
--another) another=${!pointer_plus}; ((slice_len++));;
# binary flags
-q|--quiet) quiet=true;;
-d) debug=true;;
esac
# splice out pointer frame from positional list
[[ $pointer -gt 1 ]] \
&& set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
|| set -- ${@:((pointer + $slice_len)):$#};
fi
done
# positional remain
node_name=$1
ip_address=$2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-27 02:42:47
Załóżmy, że tworzymy skrypt powłoki o nazwie test_args.sh
w następujący sposób
#!/bin/sh
until [ $# -eq 0 ]
do
name=${1:1}; shift;
if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi
done
echo "year=$year month=$month day=$day flag=$flag"
Po uruchomieniu następującego polecenia:
sh test_args.sh -year 2017 -flag -month 12 -day 22
Wyjście będzie:
year=2017 month=12 day=22 flag=true
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 22:49:30
Użyj modułu "argumenty" z bash-modules
Przykład:
#!/bin/bash
. import.sh log arguments
NAME="world"
parse_arguments "-n|--name)NAME;S" -- "$@" || {
error "Cannot parse command line."
exit 1
}
info "Hello, $NAME!"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-09 16:51:09
To również może być przydatne, aby wiedzieć, Można ustawić wartość i jeśli ktoś dostarcza dane wejściowe, nadpisać domyślną z tej wartości..
Myscript.sh -f ./ serverlisttxt lub po prostu. /myscript.sh (i przyjmuje wartości domyślne)
#!/bin/bash
# --- set the value, if there is inputs, override the defaults.
HOME_FOLDER="${HOME}/owned_id_checker"
SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"
while [[ $# > 1 ]]
do
key="$1"
shift
case $key in
-i|--inputlist)
SERVER_FILE_LIST="$1"
shift
;;
esac
done
echo "SERVER LIST = ${SERVER_FILE_LIST}"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-14 18:01:08
Inne rozwiązanie bez getopt [s], POSIX, old Unix style
Podobne do rozwiązanie Bruno Bronosky opublikował ten tutaj jest jeden bez użycia getopt(s)
.
Główną cechą odróżniającą moje rozwiązanie jest to, że pozwala ono na łączenie ze sobą opcji tak jak {[4] } jest równe tar -x -z -f foo.tar.gz
. I tak jak w tar
, ps
itd. łącznik wiodący jest opcjonalny dla bloku krótkich opcji (ale można to łatwo zmienić). Długie opcje są również obsługiwane (ale gdy blok zaczyna się od jednego, wtedy wymagane są dwa myślniki wiodące).
Kod z przykładowymi opcjami
#!/bin/sh
echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo
print_usage() {
echo "Usage:
$0 {a|b|c} [ARG...]
Options:
--aaa-0-args
-a
Option without arguments.
--bbb-1-args ARG
-b ARG
Option with one argument.
--ccc-2-args ARG1 ARG2
-c ARG1 ARG2
Option with two arguments.
" >&2
}
if [ $# -le 0 ]; then
print_usage
exit 1
fi
opt=
while :; do
if [ $# -le 0 ]; then
# no parameters remaining -> end option parsing
break
elif [ ! "$opt" ]; then
# we are at the beginning of a fresh block
# remove optional leading hyphen and strip trailing whitespaces
opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')
fi
# get the first character -> check whether long option
first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
[ "$first_chr" = - ] && long_option=T || long_option=F
# note to write the options here with a leading hyphen less
# also do not forget to end short options with a star
case $opt in
-)
# end of options
shift
break
;;
a*|-aaa-0-args)
echo "Option AAA activated!"
;;
b*|-bbb-1-args)
if [ "$2" ]; then
echo "Option BBB with argument '$2' activated!"
shift
else
echo "BBB parameters incomplete!" >&2
print_usage
exit 1
fi
;;
c*|-ccc-2-args)
if [ "$2" ] && [ "$3" ]; then
echo "Option CCC with arguments '$2' and '$3' activated!"
shift 2
else
echo "CCC parameters incomplete!" >&2
print_usage
exit 1
fi
;;
h*|\?*|-help)
print_usage
exit 0
;;
*)
if [ "$long_option" = T ]; then
opt=$(echo "$opt" | awk '{print substr($1, 2)}')
else
opt=$first_chr
fi
printf 'Error: Unknown option: "%s"\n' "$opt" >&2
print_usage
exit 1
;;
esac
if [ "$long_option" = T ]; then
# if we had a long option then we are going to get a new block next
shift
opt=
else
# if we had a short option then just move to the next character
opt=$(echo "$opt" | awk '{print substr($1, 2)}')
# if block is now empty then shift to the next one
[ "$opt" ] || shift
fi
done
echo "Doing something..."
exit 0
Przykładowe użycie można znaleźć w poniższych przykładach.
Pozycja opcji z argumentami
Jeśli ma to wartość, opcje z argumentami nie są ostatnie (muszą być tylko długie opcje). Tak więc podczas gdy np. w tar
(przynajmniej w niektórych implementacjach) opcje f
muszą być ostatnie, ponieważ następuje nazwa pliku (tar xzf bar.tar.gz
działa, ale tar xfz bar.tar.gz
nie) tutaj tak nie jest (patrz późniejsze przykłady).
Wiele opcji z argumentami
Jako kolejny bonus parametry opcji są zużywane w kolejności opcji przez parametry z wymaganymi opcjami. Wystarczy spojrzeć na wynik mojego skryptu tutaj z linii poleceń abc X Y Z
(lub -abc X Y Z
):
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
Długie opcje również konkatenowane
Możesz również mieć długie opcje w bloku opcji, biorąc pod uwagę, że występują one ostatnio w bloku. Tak więc następujące linie komend są równoważne (łącznie z kolejnością przetwarzania opcji i ich argumentów):
-cba Z Y X
cba Z Y X
-cb-aaa-0-args Z Y X
-c-bbb-1-args Z Y X -a
--ccc-2-args Z Y -ba X
c Z Y b X a
-c Z Y -b X -a
--ccc-2-args Z Y --bbb-1-args X --aaa-0-args
Wszystko to prowadzi do:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
Nie w tym roztworze
Argumenty opcjonalne
Opcje z opcjonalnymi argumentami powinny być możliwe przy odrobinie pracy, np. patrząc forward, czy istnieje blok bez myślnika; użytkownik musi wtedy umieścić myślnik przed każdym blokiem po bloku z parametrem o parametrze opcjonalnym. Być może jest to zbyt skomplikowane, aby komunikować się z użytkownikiem, więc lepiej w tym przypadku całkowicie wymagać wiodącego myślnika.
Sprawy stają się jeszcze bardziej skomplikowane z wieloma możliwymi parametrami. Ja bym odradzał dokonywanie opcji starając się być mądrym przez określenie, czy argument a może być za to lub nie (np. z opcją przyjmuje tylko liczbę jako opcjonalny argument), ponieważ może się to zepsuć w przyszłości.
Osobiście preferuję dodatkowe opcje zamiast opcjonalnych argumentów.
Argumenty opcji wprowadzone znakiem równości
Tak jak z opcjonalnymi argumentami nie jestem fanem tego (BTW, czy istnieje wątek do dyskusji na plusy / minusy różnych stylów parametrów?) ale jeśli chcesz to prawdopodobnie możesz wdrożyć to samodzielnie tak jak to robiono w http://mywiki.wooledge.org/BashFAQ/035#Manual_loop ze stwierdzeniem przypadku --long-with-arg=?*
, a następnie usunięciem znaku równości (jest to BTW strona, która mówi, że tworzenie konkatenacji parametru jest możliwe przy pewnym wysiłku, ale "zostawiono [to] jako ćwiczenie dla czytelnika", co zmusiło mnie do wzięcia ich na słowo, ale zacząłem od zera).
Inne uwagi
Zgodny z POSIX, działa nawet na starożytnych konfiguracjach Busybox, z którymi miałem do czynienia (np. cut
, head
oraz getopts
brak).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:36
Rozwiązanie, które zachowuje nieobsługiwane argumenty. W Zestawie Demo.
Oto moje rozwiązanie. Jest bardzo elastyczny i w przeciwieństwie do innych, nie powinien wymagać zewnętrznych pakietów i radzić sobie z pozostawionymi argumentami.Użycie to: ./myscript -flag flagvariable -otherflag flagvar2
Wystarczy edytować linię validflags. Dodaje myślnik i przeszukuje wszystkie argumenty. Następnie definiuje następny argument jako nazwę flagi, np.
./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2
Główny kod (wersja skrócona, z przykładami dalej w dół, także wersja z błędami):
#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
match=0
argval=$1
for flag in $validflags
do
sflag="-"$flag
if [ "$argval" == "$sflag" ]
then
declare $flag=$2
match=1
fi
done
if [ "$match" == "1" ]
then
shift 2
else
leftovers=$(echo $leftovers $argval)
shift
fi
count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
Wersja słowna z wbudowanymi demami echo:
#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
match=0
argval=$1
# argval=$(echo $@ | cut -d ' ' -f$count)
for flag in $validflags
do
sflag="-"$flag
if [ "$argval" == "$sflag" ]
then
declare $flag=$2
match=1
fi
done
if [ "$match" == "1" ]
then
shift 2
else
leftovers=$(echo $leftovers $argval)
shift
fi
count=$(($count+1))
done
#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2
echo leftovers: $leftovers
echo rate $rate time $time number $number
Ostatni, ten błąd, jeśli niepoprawny argument jest przekazywany.
#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
argval=$1
match=0
if [ "${argval:0:1}" == "-" ]
then
for flag in $validflags
do
sflag="-"$flag
if [ "$argval" == "$sflag" ]
then
declare $flag=$2
match=1
fi
done
if [ "$match" == "0" ]
then
echo "Bad argument: $argval"
exit 1
fi
shift 2
else
leftovers=$(echo $leftovers $argval)
shift
fi
count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers
Plusy: to, co robi, radzi sobie bardzo dobrze. Zachowuje nieużywane argumenty, których wiele innych rozwiązań tutaj nie ma. pozwala również na wywołanie zmiennych bez ręcznego definiowania w skrypcie. Pozwala również na prepopulację zmiennych, jeśli nie podano odpowiedniego argumentu. (Patrz verbose example).
Wady: nie można przetworzyć pojedynczego złożonego ciągu arg, np.-xcvf przetworzyłby jako pojedynczy argument. Możesz nieco łatwo napisać dodatkowy kod do mojego, który dodaje tę funkcjonalność.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-29 04:07:33
Najlepsza odpowiedź na to pytanie wydawała mi się trochę błędna, kiedy próbowałem go -- oto moje rozwiązanie, które okazało się być bardziej wytrzymałe:
boolean_arg=""
arg_with_value=""
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-b|--boolean-arg)
boolean_arg=true
shift
;;
-a|--arg-with-value)
arg_with_value="$2"
shift
shift
;;
-*)
echo "Unknown option: $1"
exit 1
;;
*)
arg_num=$(( $arg_num + 1 ))
case $arg_num in
1)
first_normal_arg="$1"
shift
;;
2)
second_normal_arg="$1"
shift
;;
*)
bad_args=TRUE
esac
;;
esac
done
# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
echo "first_normal_arg: $first_normal_arg"
echo "second_normal_arg: $second_normal_arg"
echo "boolean_arg: $boolean_arg"
echo "arg_with_value: $arg_with_value"
exit 0
fi
if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
exit 1
fi
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-01 12:09:07
Ten przykład pokazuje, jak używać getopt
i eval
oraz HEREDOC
i shift
do obsługi krótkich i długich parametrów z wymaganą wartością i bez niej. Również instrukcja switch / case jest zwięzła i łatwa do naśladowania.
#!/usr/bin/env bash
# usage function
function usage()
{
cat << HEREDOC
Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]
optional arguments:
-h, --help show this help message and exit
-n, --num NUM pass in a number
-t, --time TIME_STR pass in a time string
-v, --verbose increase the verbosity of the bash script
--dry-run do a dry run, don't change any files
HEREDOC
}
# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=
# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"
while true; do
# uncomment the next line to see how shift is working
# echo "\$1:\"$1\" \$2:\"$2\""
case "$1" in
-h | --help ) usage; exit; ;;
-n | --num ) num_str="$2"; shift 2 ;;
-t | --time ) time_str="$2"; shift 2 ;;
--dry-run ) dryrun=1; shift ;;
-v | --verbose ) verbose=$((verbose + 1)); shift ;;
-- ) shift; break ;;
* ) break ;;
esac
done
if (( $verbose > 0 )); then
# print out all the parameters we read in
cat <<-EOM
num=$num_str
time=$time_str
verbose=$verbose
dryrun=$dryrun
EOM
fi
# The rest of your script below
Najważniejsze linie powyższego skryptu to:
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"
while true; do
case "$1" in
-h | --help ) usage; exit; ;;
-n | --num ) num_str="$2"; shift 2 ;;
-t | --time ) time_str="$2"; shift 2 ;;
--dry-run ) dryrun=1; shift ;;
-v | --verbose ) verbose=$((verbose + 1)); shift ;;
-- ) shift; break ;;
* ) break ;;
esac
done
Krótki, do rzeczy, czytelny i obsługuje prawie wszystko (IMHO).
Mam nadzieję, że to komuś pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-07 18:25:44
I have write a bash helper to write a nice bash tool
Projekt domu: https://gitlab.mbedsys.org/mbedsys/bashopts
Przykład:
#!/bin/bash -ei
# load the library
. bashopts.sh
# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR
# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"
# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"
# Parse arguments
bashopts_parse_args "$@"
# Process argument
bashopts_process_args
Da pomoc:
NAME:
./example.sh - This is myapp tool description displayed on help message
USAGE:
[options and commands] [-- [extra args]]
OPTIONS:
-h,--help Display this help
-n,--non-interactive true Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
-f,--first "John" First name - [$first_name] (type:string, default:"")
-l,--last "Smith" Last name - [$last_name] (type:string, default:"")
--display-name "John Smith" Display name - [$display_name] (type:string, default:"$first_name $last_name")
--number 0 Age - [$age] (type:number, default:0)
--email Email adress - [$email_list] (type:string, default:"")
Enjoy:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-20 21:30:19
Oto moje podejście - użycie wyrażenia regularnego.
- no getopts
- obsługuje blok krótkich parametrów
-qwerty
- obsługuje krótkie parametry
-q -w -e
- obsługuje długie opcje
--qwerty
- możesz przekazać atrybut do opcji krótkiej lub długiej (jeśli używasz bloku krótkich opcji, atrybut jest dołączony do ostatniej opcji)
- możesz użyć spacji lub
=
do podania atrybutów, ale atrybuty pasują do siebie aż do napotkania myślnika+spacja "ogranicznik", więc w--q=qwe ty
qwe ty
jest jednym atrybutem - obsługuje mieszanie wszystkich powyższych, więc {[7] } jest poprawne
Skrypt:
#!/usr/bin/env sh
help_menu() {
echo "Usage:
${0##*/} [-h][-l FILENAME][-d]
Options:
-h, --help
display this help and exit
-l, --logfile=FILENAME
filename
-d, --debug
enable debug
"
}
parse_options() {
case $opt in
h|help)
help_menu
exit
;;
l|logfile)
logfile=${attr}
;;
d|debug)
debug=true
;;
*)
echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
exit 1
esac
}
options=$@
until [ "$options" = "" ]; do
if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
opt=${BASH_REMATCH[3]}
attr=${BASH_REMATCH[7]}
options=${BASH_REMATCH[9]}
elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
pile=${BASH_REMATCH[4]}
while (( ${#pile} > 1 )); do
opt=${pile:0:1}
attr=""
pile=${pile/${pile:0:1}/}
parse_options
done
opt=$pile
attr=${BASH_REMATCH[7]}
options=${BASH_REMATCH[9]}
else # leftovers that don't match
opt=${BASH_REMATCH[10]}
options=""
fi
parse_options
fi
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 16:48:58
Oto moje ulepszone rozwiązanie odpowiedzi Bruno Bronosky ' ego za pomocą zmiennych tablic.
Pozwala na mieszanie pozycji parametrów i daje tablicę parametrów zachowującą kolejność bez opcji
#!/bin/bash
echo $@
PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
-n=*|--skip=*)
SKIP+=("${i#*=}")
;;
-s|--soft)
SOFT=1
;;
*)
# unknown option
PARAMS+=("$i")
;;
esac
done
echo "SKIP = ${SKIP[@]}"
echo "SOFT = $SOFT"
echo "Parameters:"
echo ${PARAMS[@]}
Wyświetli na przykład:
$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP = .c .obj
SOFT = 1
Parameters:
parameter somefile
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-05 09:17:12
Chcę zgłosić swój projekt: https://github.com/flyingangel/argparser
source argparser.sh
parse_args "$@"
To proste. Środowisko będzie wypełnione zmiennymi o tej samej nazwie co argumentyWarning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 17:45:50