2319

たとえば、次の行で呼び出されるスクリプトがあるとします。

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

またはこれ:

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

それぞれの場合 (または 2 つの組み合わせ) $v$f、および $dがすべて に設定されtrue$outFileと等しくなるように、これを解析する受け入れられた方法は何/fizz/someOtherFileですか?

4

39 に答える 39

3234

Bash スペース区切り (例: --option argument)

cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash

POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
  case $1 in
    -e|--extension)
      EXTENSION="$2"
      shift # past argument
      shift # past value
      ;;
    -s|--searchpath)
      SEARCHPATH="$2"
      shift # past argument
      shift # past value
      ;;
    --default)
      DEFAULT=YES
      shift # past argument
      ;;
    -*|--*)
      echo "Unknown option $1"
      exit 1
      ;;
    *)
      POSITIONAL_ARGS+=("$1") # save positional arg
      shift # past argument
      ;;
  esac
done

set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
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
EOF

chmod +x /tmp/demo-space-separated.sh

/tmp/demo-space-separated.sh -e conf -s /etc /etc/hosts
上記のブロックをコピーして貼り付けた結果
FILE EXTENSION  = conf
SEARCH PATH     = /etc
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com
使用法
demo-space-separated.sh -e conf -s /etc /etc/hosts

Bash 等号区切り (例: --option=argument)

cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/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
      ;;
    --default)
      DEFAULT=YES
      shift # past argument with no value
      ;;
    -*|--*)
      echo "Unknown option $i"
      exit 1
      ;;
    *)
      ;;
  esac
done

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
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
EOF

chmod +x /tmp/demo-equals-separated.sh

/tmp/demo-equals-separated.sh -e=conf -s=/etc /etc/hosts
上記のブロックをコピーして貼り付けた結果
FILE EXTENSION  = conf
SEARCH PATH     = /etc
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com
使用法
demo-equals-separated.sh -e=conf -s=/etc /etc/hosts

よりよく理解するには、このガイド${i#*=}で「部分文字列の削除」を検索してください。これは、どちらが不要なサブプロセスを呼び出すか、またはどちらが2 つの不要なサブプロセスを呼び出すかと機能的に同等です。`sed 's/[^=]*=//' <<< "$i"``echo "$i" | sed 's/[^=]*=//'`


getopt[s] で bash を使用する

getopt(1) の制限 (古い、比較的最近のgetoptバージョン):

  • 空の文字列である引数を処理できません
  • 空白が埋め込まれた引数を処理できません

最近のgetoptバージョンには、これらの制限はありません。詳細については、これらのドキュメントを参照してください。


POSIX getopts

getoptsさらに、これらの制限のない POSIX シェルやその他の製品が提供されています。簡単な例を含めましたgetopts

cat >/tmp/demo-getopts.sh <<'EOF'
#!/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: $@"
EOF

chmod +x /tmp/demo-getopts.sh

/tmp/demo-getopts.sh -vf /etc/hosts foo bar
上記のブロックをコピーして貼り付けた結果
verbose=1, output_file='/etc/hosts', Leftovers: foo bar
使用法
demo-getopts.sh -vf /etc/hosts foo bar

の利点は次のgetoptsとおりです。

  1. 移植性が高く、 などの他のシェルで動作しdashます。
  2. -vf filename典型的な Unix の方法のように、複数の単一オプションを自動的に処理できます。

の欠点は、追加のコードなしでは短いオプション (ではなく)getoptsしか処理できないことです。-h--help

すべての構文と変数の意味を説明するgetopts チュートリアルがあります。bash には もありhelp getopts、参考になるかもしれません。

于 2013-01-07T20:01:05.670 に答える
681

強化された getoptを紹介する回答はありません。そして、トップ投票の回答は誤解を招くものです。-⁠vfdスタイルの短いオプション (OP によって要求される) または位置引数の後のオプション (OP によって要求される) を無視します。解析エラーを無視します。その代わり:

  • getoptutil-linux または以前の GNU glibc から拡張されたものを使用します。1
  • getopt_long()GNU glibc の C 関数で動作します。
  • このページの他のソリューションでは、これをすべて行うことはできません:
    • 引数2でスペース、引用符、さらにはバイナリを処理します(拡張されgetoptていないものはこれを行うことができません)
    • 最後にオプションを処理できます: script.sh -o outFile file1 file2 -v(getoptsこれは行いません)
    • =-style long オプションを許可: (script.sh --outfile=fileOut --infile fileIn自己解析の場合、両方を許可すると時間がかかります)
    • 組み合わせた短いオプションを許可します。たとえば、-vfd(自己解析の場合の実際の作業)
    • オプション引数に触れることができます-oOutfile-vfdoOutfile
  • すでに3非常に古いため、GNU システムにはこれがありません (たとえば、どの Linux にもあります)。
  • その存在をテストするには: getopt --test→ 戻り値 4.
  • その他getoptまたはシェル組み込みgetoptsの用途は限られています。

以下の呼び出し

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

すべて戻る

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

以下でmyscript

#!/bin/bash
# More safety, by turning some bugs into errors.
# Without `errexit` you don’t need ! and can replace
# PIPESTATUS with a simple $?, but I don’t do that.
set -o errexit -o pipefail -o noclobber -o nounset

# -allow a command to fail with !’s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! 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

# -regarding ! and PIPESTATUS see above
# -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強化された getopt は、Cygwin を含むほとんどの「bash システム」で利用できます。OS X では、 brew install gnu-getoptまたはsudo port install getopt
2を試してください。POSIXexec()規則には、コマンド ライン引数でバイナリ NULL を渡す信頼できる方法がありません。これらのバイトは、1997 年以前にリリースされ
た引数3の最初のバージョンを早期に終了します

于 2015-04-20T17:47:25.610 に答える
149

マイナーな変更を加えたdigitalpeer.comから:

使用法 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}

よりよく理解するには、このガイド${i#*=}で「部分文字列の削除」を検索してください。これは、どちらが不要なサブプロセスを呼び出すか、またはどちらが2 つの不要なサブプロセスを呼び出すかと機能的に同等です。`sed 's/[^=]*=//' <<< "$i"``echo "$i" | sed 's/[^=]*=//'`

于 2012-11-13T10:31:07.720 に答える
111
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

このソリューション:

  • ハンドル-n arg--name=arg
  • 最後に引数を許可します
  • 何かスペルが間違っている場合は、正常なエラーを表示します
  • 互換性があり、バシズムを使用しません
  • 読みやすく、ループで状態を維持する必要はありません
于 2015-07-15T23:43:36.203 に答える
109

getopt()/getopts()は良いオプションです。ここからコピー:

「getopt」の簡単な使用法は、次のミニスクリプトに示されています。

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

-a、-b、-c、または -d のいずれも許可されますが、-c の後には引数が続きます ("c:" はそのことを示しています)。

これを「g」と呼んで試してみると:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

2 つの引数から始めます。"getopt" はオプションを分解し、それぞれを独自の引数に入れます。また、「--」を追加しました。

于 2008-10-10T17:03:38.463 に答える
46

移植可能な構文解析をスクリプトで記述する問題が非常にイライラするので、Argbashを作成しました。これは、スクリプトの引数構文解析コードを生成できる FOSS コード ジェネレーターであり、いくつかの優れた機能を備えています。

https://argbash.io

于 2016-07-10T22:40:42.817 に答える
16

これは使いやすいと思います:

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

呼び出し例:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
于 2012-03-01T15:15:03.550 に答える
12

parse_paramsコマンドラインからパラメーターを解析する関数を提供します。

  1. これは純粋な Bash ソリューションであり、追加のユーティリティはありません。
  2. グローバル スコープを汚染しません。
  3. 簡単に使用できる変数を簡単に返し、さらにロジックを構築できます。
  4. params の前のダッシュの量は重要ではありません ( --allequals -allequals all=all)

以下のスクリプトは、コピー アンド ペーストの動作デモです。関数を参照show_useして、使用方法を理解してくださいparse_params

制限:

  1. スペースで区切られたパラメーター ( -d 1)はサポートされていません
  2. パラメータ名はダッシュを失うので--any-param-anyparam同等です
  3. eval $(parse_params "$@")bash関数内で使用する必要があります(グローバル スコープでは機能しません)。

#!/bin/bash

# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# 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)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-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, and quotes to prevent string breakage
        _escaped=${1/\*/\'\"*\"\'}
        _escaped=${_escaped//\'/\\\'}
        _escaped=${_escaped//\"/\\\"}
        # If equals delimited named parameter
        nonspace="[^[:space:]]"
        if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # 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
            # 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" =~ ^\-${nonspace}+ ]]; then
            # remove dashes
            local _key=${1//\-}
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                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
于 2016-07-01T20:56:37.003 に答える
11

getopts は、#1 インストール済みであり、#2 同じプラットフォームで実行するつもりであれば、うまく機能します。OSX と Linux (たとえば) では、この点で動作が異なります。

これは、equals、non-equals、および boolean フラグをサポートする (getopts 以外の) ソリューションです。たとえば、次の方法でスクリプトを実行できます。

./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
于 2015-02-12T21:50:26.093 に答える
8

プロジェクトを提出したい: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "$@"

そのような単純な。環境には、引数と同じ名前の変数が設定されます

于 2018-09-16T17:45:50.463 に答える
7

以下を可能にするオプション解析のバージョンを提供したいと思います。

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

これも可能です(望ましくない可能性があります):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

= をオプションで使用するかどうかは、使用前に決定する必要があります。これは、コードをきれいに (っぽい) 保つためです。

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
于 2015-06-24T10:54:23.253 に答える
6

cmdline 引数を解析する方法はいくつかあります (例: GNU getopt (移植不可) vs BSD (MacOS) getopt vs getopts) - すべて問題があります。このソリューション

  • ポータブルです!
  • 依存関係がなく、bash ビルトインのみに依存
  • 短いオプションと長いオプションの両方が可能
  • =空白を処理するか、オプションと引数の間のセパレーターを同時に使用する
  • 連結された短いオプション スタイルをサポート-vxf
  • オプションの引数を持つオプションを処理します (例--colorvs --color=always)、
  • 不明なオプションを正しく検出して報告する
  • --オプションの終了を通知するサポート、および
  • 同じ機能セットの代替と比較して、コードの肥大化は必要ありません。つまり、簡潔であるため、保守が容易です

例: 以下のいずれか

# flag
-f
--foo

# option with required argument
-b"Hello World"
-b "Hello World"
--bar "Hello World"
--bar="Hello World"

# option with optional argument
--baz
--baz="Optional Hello"

#!/usr/bin/env bash

usage() {
  cat - >&2 <<EOF
NAME
    program-name.sh - Brief description
 
SYNOPSIS
    program-name.sh [-h|--help]
    program-name.sh [-f|--foo]
                    [-b|--bar <arg>]
                    [--baz[=<arg>]]
                    [--]
                    FILE ...

REQUIRED ARGUMENTS
  FILE ...
          input files

OPTIONS
  -h, --help
          Prints this and exits

  -f, --foo
          A flag option
      
  -b, --bar <arg>
          Option requiring an argument <arg>

  --baz[=<arg>]
          Option that has an optional argument <arg>. If <arg>
          is not specified, defaults to 'DEFAULT'
  --     
          Specify end of options; useful if the first non option
          argument starts with a hyphen

EOF
}

fatal() {
    for i; do
        echo -e "${i}" >&2
    done
    exit 1
}

# For long option processing
next_arg() {
    if [[ $OPTARG == *=* ]]; then
        # for cases like '--opt=arg'
        OPTARG="${OPTARG#*=}"
    else
        # for cases like '--opt arg'
        OPTARG="${args[$OPTIND]}"
        OPTIND=$((OPTIND + 1))
    fi
}

# ':' means preceding option character expects one argument, except
# first ':' which make getopts run in silent mode. We handle errors with
# wildcard case catch. Long options are considered as the '-' character
optspec=":hfb:-:"
args=("" "$@")  # dummy first element so $1 and $args[1] are aligned
while getopts "$optspec" optchar; do
    case "$optchar" in
        h) usage; exit 0 ;;
        f) foo=1 ;;
        b) bar="$OPTARG" ;;
        -) # long option processing
            case "$OPTARG" in
                help)
                    usage; exit 0 ;;
                foo)
                    foo=1 ;;
                bar|bar=*) next_arg
                    bar="$OPTARG" ;;
                baz)
                    baz=DEFAULT ;;
                baz=*) next_arg
                    baz="$OPTARG" ;;
                -) break ;;
                *) fatal "Unknown option '--${OPTARG}'" "see '${0} --help' for usage" ;;
            esac
            ;;
        *) fatal "Unknown option: '-${OPTARG}'" "See '${0} --help' for usage" ;;
    esac
done

shift $((OPTIND-1))

if [ "$#" -eq 0 ]; then
    fatal "Expected at least one required argument FILE" \
    "See '${0} --help' for usage"
fi

echo "foo=$foo, bar=$bar, baz=$baz, files=${@}"
于 2019-12-24T02:09:42.760 に答える
5

未処理の引数を保持するソリューション。デモが含まれています。

これが私の解決策です。非常に柔軟で、他のパッケージとは異なり、外部パッケージを必要とせず、残りの引数をきれいに処理します。

使用法は次のとおりです。./myscript -flag flagvariable -otherflag flagvar2

必要なのは、validflags 行を編集することだけです。ハイフンを先頭に追加し、すべての引数を検索します。次に、次の引数をフラグ名として定義します。

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

メインコード (短いバージョン、さらに下に例を含む詳細、エラーが発生したバージョン):

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

エコーデモが組み込まれた詳細バージョン:

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

最後に、無効な -argument が渡された場合、これはエラーになります。

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

長所:何をするか、それは非常にうまく処理します。ここでの他の多くのソリューションでは保持されない未使用の引数が保持されます。また、スクリプトで手動で定義せずに変数を呼び出すこともできます。対応する引数が指定されていない場合は、変数の事前設定も許可されます。(詳細な例を参照してください)。

短所: 単一の複雑な引数文字列を解析できません。たとえば、-xcvf は単一の引数として処理されます。ただし、この機能を追加する追加のコードを私のコードに簡単に書き込むことができます。

于 2016-08-29T03:44:56.437 に答える
4

getopt(1)これは AT&T の短命の誤りであることに注意してください。

getopt は 1984 年に作成されましたが、実際には使用できなかったため、1986 年には既に埋もれていました。

getopt非常に時代遅れであるという事実の証拠は、 getopt(1)man ページがの"$*"代わりにまだ言及していることです"$@"。これは、1986 年にシェル ビルトインと共に Bourne Shell に追加され、getopts(1)内部のスペースを含む引数を処理します。

ところで: シェル スクリプトで長いオプションを解析することに興味がある場合はgetopt(3)、libc (Solaris) からの実装とksh93両方が、長いオプションを短いオプションのエイリアスとしてサポートする均一な長いオプションの実装を追加したことを知っておくとよいでしょう。これによりksh93、 と は、Bourne Shellを介して長いオプション用の統一インターフェイスを実装しますgetopts

Bourne Shell の man ページから取った長いオプションの例:

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

Bourne Shell と ksh93 の両方でオプション エイリアスを使用できる期間を示します。

最近の Bourne Shell の man ページを参照してください。

http://schillix.sourceforge.net/man/man1/bosh.1.html

OpenSolaris の getopt(3) の man ページ:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

そして最後に、古い $* を検証するための getopt(1) man ページ:

http://schillix.sourceforge.net/man/man1/getopt.1.html

于 2015-10-19T13:59:34.890 に答える
3

位置引数とフラグベースの引数の混合

--param=arg (等号区切り)

位置引数間でフラグを自由に組み合わせる:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

かなり簡潔なアプローチで実現できます。

# 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 (スペース区切り)

--flag=value通常は、ミックスして--flag valueスタイルしない方が明確です。

./script.sh dumbo 127.0.0.1 --environment production -q -d

これは読むのが少し危険ですが、それでも有効です

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

ソース

# 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
于 2015-04-27T02:42:47.553 に答える
3

解析オプションのために作成したものを共有したいと思いました。ここでの回答では私のニーズの一部が満たされなかったので、これを考え出す必要がありました: https://github.com/MihirLuthra/bash_option_parser

これは以下をサポートします。

  • サブオプションの解析
  • オプションの別名
  • オプションの引数
  • 可変引数
  • 印刷の使い方とエラー

次のような使用方法で名前が付けられたコマンドがあるとfruitします。

fruit <fruit-name> ...
   [-e|—-eat|—-chew]
   [-c|--cut <how> <why>]
   <command> [<args>] 

-e引数を取らない
-c2 つの引数を取ります。つまり、how to cut と why to cut
fruit自体は、少なくとも 1 つの引数を取ります。などのサブオプション用です(サブ
<command>オプションがあるのと同様です)appleorangegitcommitpush

したがって、それを解析するには:

parse_options \
    'fruit'                         '1 ...'  \
    '-e'     , '--eat' , '--chew'   '0'      \
    '-c'     , '--cut'              '1 1'    \
    'apple'                         'S'      \
    'orange'                        'S'      \
    ';' \
    "$@"

option_parser_error_msg使用上のエラーがあった場合は、次のように印刷できます。

retval=$?

if [ $retval -ne 0 ]; then
    # this will manage error messages if
    # insufficient or extra args are supplied

    option_parser_error_msg "$retval"

    # This will print the usage
    print_usage 'fruit'
    exit 1
fi

いくつかのオプションが渡されたかどうかを確認するには、

if [ -n "${OPTIONS[-c]}" ]
then
    echo "-c was passed"

    # args can be accessed in a 2D-array-like format
    echo "Arg1 to -c = ${ARGS[-c,0]}"
    echo "Arg2 to -c = ${ARGS[-c,1]}"

fi

サブオプションの解析は、サブオプションの args に達するまで引数をシフトした後に解析を開始$shift_countするwhich を渡すことによっても実行できます。parse_options_detailedこのでそれを示します。

詳細な説明は、リポジトリの readme と例に記載されています。

于 2020-04-10T12:12:17.030 に答える
2

bash-modules のモジュール「引数」を使用する

例:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"
于 2013-07-09T16:51:09.393 に答える
2

ここでの他の回答に基づいて、これは私のバージョンです:

#!/bin/bash
set -e

function parse() {
    for arg in "$@"; do # transform long options to short ones
        shift
        case "$arg" in
            "--name") set -- "$@" "-n" ;;
            "--verbose") set -- "$@" "-v" ;;
            *) set -- "$@" "$arg"
        esac
    done

    while getopts "n:v" optname
    do
        case "$optname" in
            "n") name=${OPTARG} ;;
            "v") verbose=true ;;
        esac
    done
    shift "$((OPTIND-1))" # shift out all the already processed options
}


parse "$@"
echo "hello $name"
if [ ! -z $verbose ]; then echo 'nice to meet you!'; fi

使用法:

$ ./parse.sh
hello
$ ./parse.sh -n YOUR_NAME
hello YOUR_NAME
$ ./parse.sh -n YOUR_NAME -v
hello YOUR_NAME
nice to meet you!
$ ./parse.sh -v -n YOUR_NAME
hello YOUR_NAME
nice to meet you!
$ ./parse.sh -v
hello 
nice to meet you!
于 2021-10-27T10:26:48.120 に答える
1

これは、変数配列を使用したBruno Bronoskyの回答の改善されたソリューションです。

パラメータの位置を混在させ、オプションなしで順序を維持するパラメータ配列を与えることができます

#!/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[@]}

たとえば、次のように出力されます。

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile
于 2015-10-06T08:53:32.507 に答える
1

この質問に対する一番の答えは、私が試したときに少しバグがあるように見えました-より堅牢であることがわかった私の解決策は次のとおりです。

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
于 2016-08-08T12:42:47.190 に答える
1

これも知っておくと役立つ場合があります。値を設定し、誰かが入力を提供した場合は、デフォルトをその値で上書きできます。

myscript.sh -f ./serverlist.txt または単に./myscript.sh (デフォルトが必要です)

    #!/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}"
于 2014-06-14T18:01:08.403 に答える
1

最後からkey => valueを反復するために使用します。最初の省略可能な引数は、ループの後にキャッチされます。

使用法は ./script.sh optional-first-arg -key value -key2 value2 です

#!/bin/sh

a=$(($#-1))
b=$(($#))
while [ $a -gt 0 ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b-2))
    a=$(($a-2))
done
unset a b key value

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

確かに、いくつかの変更を加えると、左から右にそれを行うことができます。

このスニペット コードは、キー => 値のペアと、存在する場合は最初の引数を示しています。

#!/bin/sh

a=$((1+$#%2))
b=$((1+$a))

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

while [ $a -lt $# ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b+2))
    a=$(($a+2))
done

unset a b key value

100,000 個の引数でテスト済み、高速。

eval を使用せずに、 key => value最初のオプションの argを左から右に反復することもできます。

#!/bin/sh

a=$(($#%2))
b=0

[ $a -eq 1 ] && echo "first_arg = $1"

for value; do
    if [ $b -gt $a -a $(($b%2)) -ne $a ]; then
        echo "$key => $value"
    fi
    key="$value"
    b=$((1+$b))
done

unset a b key value

于 2021-03-10T21:32:26.697 に答える