2013-02-09 19 views
9

Po kilku dniach od research nadal nie mogę znaleźć najlepszej metody analizowania argumentów cmdline w skrypcie .sh. Według moich odniesień getopts cmd jest droga, ponieważ „ekstraktów i sprawdza przełączników bez zakłócania parametru pozycyjnego przełączniki variables.Unexpected lub przełączników, których brakuje argumentów, są ujmowane i reportedas błędów.Najlepszy sposób analizowania argumentów linii poleceń w Bashu?

pozycyjne parametry (np. 2 - $ @, $ #, itp.) najwyraźniej nie działają dobrze, gdy są zaangażowane spacje, ale potrafią rozpoznać parametry regularne i długie (-p i --longparam). Zauważyłem, że obie metody zawodzą przy przekazywaniu parametrów z zagnieżdżonymi cudzysłowami ("to jest przykład z" "cytuje" "."). Która z tych trzech próbek kodu najlepiej ilustruje sposób radzenia sobie z argumentami cmdline? Funkcja getopt nie jest polecana przez guru, więc staram się tego uniknąć!

Przykład 1:

#!/bin/bash 
for i in "[email protected]" 
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 

Przykład 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 [email protected]’ 
let i=1 
for x in [email protected]; 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 [email protected] 

using $* 

using shift 
#bash> commandLineArguments.bash "abc def" g h i j* 

Przykład 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 
+1

możliwy duplikat [Jak analizować argumenty linii komend w bash?] (Http://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash) –

Odpowiedz

18

znaleźć zastosowanie getopt być najłatwiejszym. Zapewnia poprawną obsługę argumentów, co w innych przypadkach jest trudne. Na przykład getopt będzie wiedział, jak obsługiwać argumenty do długiej opcji określonej w wierszu poleceń jako --arg=option lub --arg option.

Przydatny w analizie danych wejściowych przekazywanych do skryptu powłoki jest użycie zmiennych "[email protected]". Zobacz stronę man, aby dowiedzieć się, jak to się różni od [email protected]. Zapewnia przetwarzanie argumentów zawierających spacje.

Oto przykład, w jaki sposób mogę napisać ów skrypt do analizowania proste argumenty wiersza polecenia:

#!/bin/bash 

args=$(getopt -l "searchpath:" -o "s:h" -- "[email protected]") 

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: $*" 

i używane tak, aby pokazać, że przestrzenie i cytaty są zachowane:

[email protected]:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"." 
searchpath: File with spaces and "quotes". 
remaining args: other args 

Niektóre podstawowe informacje na temat korzystania z getopt można znaleźć here

+2

To jest dobry przykład z getopt Austin. Ten konkretny temat był szeroko dyskutowany na [stackoverflow] (http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line- opcje/7680682 # 7680682). Różnica polega na tym, że stary getopt nie jest tak wytrzymały jak getopts i nie działa na starszych systemach. Getopts może analizować długie przełączniki tak długo, jak istnieje krótka wersja tego samego przełącznika, więc wymaga to nieco ulepszenia. Będę trzymać się/getopts. Raczej korzystam z wbudowanej funkcji niż starego exec (getopt) dla profesjonalnych skryptów. – LogicalConfusion

0

Jeśli chcesz uniknąć korzystania z getopt n użyj tego fajnego szybkiego podejścia:
- Definiowanie pomocy dla wszystkich opcji jako ## komentarze (dostosuj, jak chcesz);
- Dla każdej opcji należy zdefiniować funkcję o tej samej nazwie;
- Skopiuj ostatnie pięć linii tego skryptu do skryptu (magia).

Przykład: 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 [email protected] >&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#--}(?:[= ])" go.sh | sed -e "s/-/_/g") 
    if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi 
    shift; eval "$CMD" [email protected] || shift $? 2> /dev/null 
done 

Testowanie:

./log.sh --log yep --log-quiet -l nop -i -l yes będzie produkować:

yep 
yes 

Przy okazji: Jest to zgodne z POSIX!