2542 Stimmen

Wie analysiere ich Kommandozeilenargumente in der Bash?

Angenommen, ich habe ein Skript, das mit dieser Zeile aufgerufen wird:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

oder dieses:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Wie kann man dies so interpretieren, dass in jedem Fall (oder einer Kombination von beiden) $v , $f y $d werden alle auf true y $outFile wird gleich sein mit /fizz/someOtherFile ?

1 Stimmen

Für zsh-Benutzer gibt es ein großartiges builtin namens zparseopts, das das kann: zparseopts -D -E -M -- d=debug -debug=d Und haben beide -d y --debug im $debug Array echo $+debug[1] gibt 0 oder 1 zurück, wenn einer dieser Werte verwendet wird. Ref: zsh.org/mla/users/2011/msg00350.html

2 Stimmen

Ein wirklich gutes Tutorial: linuxcommand.org/lc3_wss0120.php . Besonders gut gefällt mir das Beispiel "Befehlszeilenoptionen".

0 Stimmen

Ich habe ein Skript erstellt, das dies für Sie erledigt, es heißt - github.com/unfor19/bargs

33voto

phyatt Punkte 17614

Dieses Beispiel zeigt, wie man getopt y eval y HEREDOC y shift um kurze und lange Parameter mit und ohne einen nachfolgenden erforderlichen Wert zu behandeln. Auch die switch/case-Anweisung ist prägnant und leicht zu verstehen.

#!/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, dont 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

Die wichtigsten Zeilen des obigen Skripts sind diese:

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

Kurz, prägnant, gut lesbar und behandelt so ziemlich alles (IMHO).

Ich hoffe, das hilft jemandem.

4 Stimmen

Dies ist eine der besten Antworten.

25voto

jchook Punkte 5914

In Erweiterung der Antwort von @bruno-bronosky habe ich einen "Präprozessor" hinzugefügt, der einige gängige Formatierungen übernimmt:

  • Erweitert --longopt=val in --longopt val
  • Erweitert -xyz in -x -y -z
  • Unterstützt -- um das Ende der Flaggen anzuzeigen
  • Zeigt einen Fehler für unerwartete Optionen an
  • Kompakter und leicht ablesbarer Optionsschalter

    !/bin/bash

    Report usage

    usage() { echo "Usage:" echo "$(basename "$0") [options] [--] [file1, ...]" }

    invalid() { echo "ERROR: Unrecognized argument: $1" >&2 usage exit 1 }

    Pre-process options to:

    - expand -xyz into -x -y -z

    - expand --longopt=arg into --longopt arg

    ARGV=() END_OF_OPT= while [[ $# -gt 0 ]]; do arg="$1"; shift case "${END_OF_OPT}${arg}" in --) ARGV+=("$arg"); END_OF_OPT=1 ;; --=)ARGV+=("${arg%%=}" "${arg#=}") ;; --) ARGV+=("$arg") ;; -) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;; *) ARGV+=("$arg") ;; esac done

    Apply pre-processed options

    set -- "${ARGV[@]}"

    Parse options

    END_OF_OPT= POSITIONAL=() while [[ $# -gt 0 ]]; do case "${END_OF_OPT}${1}" in -h|--help) usage; exit 0 ;; -p|--password) shift; PASSWORD="$1" ;; -u|--username) shift; USERNAME="$1" ;; -n|--name) shift; names+=("$1") ;; -q|--quiet) QUIET=1 ;; -C|--copy) COPY=1 ;; -N|--notify) NOTIFY=1 ;; --stdin) READ_STDIN=1 ;; --) END_OF_OPT=1 ;; -) invalid "$1" ;; ) POSITIONAL+=("$1") ;; esac shift done

    Restore positional parameters

    set -- "${POSITIONAL[@]}"

0 Stimmen

Das sieht toll aus - aber ich frage mich, ob END_OF_OPT=1 ist in dieser Zeile tatsächlich notwendig: --*) ARGV+=("$arg"); END_OF_OPT=1 ;; . Wenn es dort belassen wird, schlägt das Parsen fehl --username=fred wenn es nach --quiet (oder jede andere boolesche Option im langen Stil). Zum Beispiel, script.sh --quiet --username=fred scheitert mit Unrecognized argument: --username=fred (obwohl script.sh --quiet --username fred funktioniert gut). Ich habe das herausgenommen END_OF_OPT=1 in meinem Skript und jetzt funktioniert es, aber ich bin mir nicht sicher, ob das vielleicht ein anderes Szenario stört, das mir nicht bekannt ist.

21voto

unsynchronized Punkte 4729

Wenn Sie Skripte erstellen, die mit anderen Hilfsprogrammen austauschbar sind, kann die folgende Flexibilität nützlich sein.

Entweder:

command -x=myfilename.ext --another_switch 

Oder:

command -x myfilename.ext --another_switch

Hier ist der Code:

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

17voto

Alek Punkte 547

Ich denke, dieser ist einfach genug zu benutzen:

#!/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

Beispiel für eine Aufforderung:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile

1 Stimmen

Ich habe alle gelesen, und dieses ist mein Lieblingsbuch. Ich benutze nicht gerne -a=1 als argc-Stil. Ich ziehe es vor, zuerst die Hauptoptionen -options und danach die speziellen Optionen mit einfachem Abstand zu setzen -o option . Ich suche nach dem einfachsten-vs-besseren Weg, argvs zu lesen.

1 Stimmen

Es funktioniert wirklich gut, aber wenn Sie ein Argument an eine nicht a: Option übergeben, werden alle folgenden Optionen als Argumente genommen. Sie können diese Zeile überprüfen ./myscript -v -d fail -o /fizz/someOtherFile -f ./foo/bar/someFile mit Ihrem eigenen Skript. Die Option -d ist nicht auf d gesetzt:

13voto

Koichi Nakashima Punkte 591

Ein weiterer Optionsparser (Generator)

Ein eleganter Optionsparser für Shell-Skripte (volle Unterstützung für alle POSIX-Shells) https://github.com/ko1nksm/getoptions (Update: v3.3.0 veröffentlicht am 2021-05-02)

getopt ist ein neuer Optionsparser (Generator), der in einem POSIX-konformen Shell-Skript geschrieben und im August 2020 veröffentlicht wurde. Es ist für diejenigen, die die POSIX / GNU-Stil Option Syntax in Ihren Shell-Skripten unterstützen wollen.

Die unterstützten Syntaxen sind -a , +a , -abc , -vvv , -p VALUE , -pVALUE , --flag , --no-flag , --with-flag , --without-flag , --param VALUE , --param=VALUE , --option[=VALUE] , --no-option -- .

Es unterstützt Unterbefehle, Validierung, abgekürzte Optionen und automatische Hilfegenerierung. Und funktioniert mit allen POSIX-Shells (dash 0.5.4+, bash 2.03+, ksh88+, mksh R28+, zsh 3.1.9+, yash 2.29+, busybox ash 1.1.3+, etc).

#!/bin/sh

VERSION="0.1"

parser_definition() {
  setup   REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
  msg -- 'Options:'
  flag    FLAG    -f --flag                 -- "takes no arguments"
  param   PARAM   -p --param                -- "takes one argument"
  option  OPTION  -o --option on:"default"  -- "takes one optional argument"
  disp    :usage  -h --help
  disp    VERSION    --version
}

eval "$(getoptions parser_definition) exit 1"

echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments

Es analysiert die folgenden Argumente:

example.sh -f --flag -p VALUE --param VALUE -o --option -oVALUE --option=VALUE 1 2 3

Und automatische Hilfegenerierung.

$ example.sh --help

Usage: example.sh [options]... [arguments]...

Options:
  -f, --flag                  takes no arguments
  -p, --param PARAM           takes one argument
  -o, --option[=OPTION]       takes one optional argument
  -h, --help
      --version

Es ist auch ein Optionsparser-Generator, der den folgenden einfachen Optionsparser-Code erzeugt. Wenn Sie den generierten Code verwenden, brauchen Sie nicht getoptions . Erzielen Sie echte Portabilität und keine Abhängigkeit.

FLAG=''
PARAM=''
OPTION=''
REST=''
getoptions_parse() {
  OPTIND=$(($#+1))
  while OPTARG= && [ $# -gt 0 ]; do
    case $1 in
      --?*=*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
        ;;
      --no-*|--without-*) unset OPTARG ;;
      -[po]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
        ;;
      -[fh]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
        OPTARG= ;;
    esac
    case $1 in
      '-f'|'--flag')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
        FLAG="$OPTARG"
        ;;
      '-p'|'--param')
        [ $# -le 1 ] && set "required" "$1" && break
        OPTARG=$2
        PARAM="$OPTARG"
        shift ;;
      '-o'|'--option')
        set -- "$1" "$@"
        [ ${OPTARG+x} ] && {
          case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
          [ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='default'
        } || OPTARG=''
        OPTION="$OPTARG"
        shift ;;
      '-h'|'--help')
        usage
        exit 0 ;;
      '--version')
        echo "${VERSION}"
        exit 0 ;;
      --)
        shift
        while [ $# -gt 0 ]; do
          REST="${REST} \"\${$(($OPTIND-$#))}\""
          shift
        done
        break ;;
      [-]?*) set "unknown" "$1"; break ;;
      *)
        REST="${REST} \"\${$(($OPTIND-$#))}\""
    esac
    shift
  done
  [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
  case $1 in
    unknown) set "Unrecognized option: $2" "$@" ;;
    noarg) set "Does not allow an argument: $2" "$@" ;;
    required) set "Requires an argument: $2" "$@" ;;
    pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
    notcmd) set "Not a command: $2" "$@" ;;
    *) set "Validation error ($1): $2" "$@"
  esac
  echo "$1" >&2
  exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage: example.sh [options]... [arguments]...

Options:
  -f, --flag                  takes no arguments
  -p, --param PARAM           takes one argument
  -o, --option[=OPTION]       takes one optional argument
  -h, --help
      --version
GETOPTIONSHERE
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X