bash getopts、短いオプションのみ、すべて値が必要です。

bash getopts、短いオプションのみ、すべて値が必要です。

さまざまなオプションを受け入れるシェルスクリプトを作成しようとしていますが、オプションと引数の可変getopts順序を処理できるため、良い解決策のようです。

次のように、各オプションに対応する値を必要とする短いオプションのみを使用します。./command.sh -a arga -g argg -b argbしかし、オプションを非特定の順序で入力できるようにしたいと思います。これは、ほとんどの人がシェルコマンドを使用するのに慣れています。

caseもう一つのことは、オプションパラメータ値を直接、できるだけ明細書で確認したいという点だ。その理由は、ステートメント:)に対する私のテストが一貫性のない結果をもたらしたからです(おそらく私の理解の欠如によるものです)。たとえば、case

#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
        echo "Usage"
        exit 2
fi
while getopts ":h:u:p:d:" opt; do
        case "$opt" in

                h)
                        MYSQL_HOST=$OPTARG
                        ;;
                u)
                        MYSQL_USER=$OPTARG
                        ;;
                p)
                        MYSQL_PASS=$OPTARG
                        ;;
                d)
                        BACKUP_DIR=$OPTARG
                        ;;
                \?)
                        echo "Invalid option: -$OPTARG" >&2
                        exit 2;;
                :)
                       echo "Option -$OPTARG requires an argument" >&2
                       exit 2;;
        esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST'  MYSQL_USER='$MYSQL_USER'  MYSQL_PASS='$MYSQL_PASS'  BACKUP_DIR='$BACKUP_DIR' Additionals: $@"

何が起こるかはこれが失敗するということです... -dに引数が必要ですが、私が取得する値が私が必要な値ではないという./command.sh -d -h
フラグを指定したいときです。-d=-h

だから、各オプションが設定され、一度だけ設定されていることを確認するために、Caseステートメントで独自の検証を実行する方が簡単になると思いました。

以下を実行しようとしていますが、私のif [ ! "$MYSQL_HOST" ]; thenブロックは実行されません。

OPTIND=1 # Reset if getopts used previously

if (($# == 0)); then
        echo "Usage"
        exit 2
fi

while getopts ":h:u:p:d:" opt; do
        case "$opt" in

                h)
                        MYSQL_HOST=$OPTARG
                        if [ ! "$MYSQL_HOST" ]; then
                                echo "host not set"
                                exit 2
                        fi
                        ;;
                u)
                        MYSQL_USER=$OPTARG
                        if [ ! "$MYSQL_USER" ]; then
                                echo "username not set"
                                exit 2
                        fi
                        ;;
                p)
                        MYSQL_PASS=$OPTARG
                        if [ ! "$MYSQL_PASS" ]; then
                                echo "password not set"
                                exit 2
                        fi
                        ;;
                d)
                        BACKUP_DIR=$OPTARG
                        if [ ! "$BACKUP_DIR" ]; then
                                echo "backup dir not set"
                                exit 2
                        fi
                        ;;
                \?)
                        echo "Invalid option: -$OPTARG" >&2
                        exit 2;;
                #:)
                #       echo "Option -$opt requires an argument" >&2
                #       exit 2;;
        esac
done
shift $((OPTIND-1))

echo "MYSQL_HOST='$MYSQL_HOST'  MYSQL_USER='$MYSQL_USER'  MYSQL_PASS='$MYSQL_PASS'  BACKUP_DIR='$BACKUP_DIR' Additionals: $@"

OPTARGan内の長さが0であることを確認する方法はありませんかgetopts ... while ... case

外部でパラメータ検証を実行することに頼りたくありません。これにより、etcgetoptsのパラメータ値を取得し、欠落しているオプションをキャプチャできない可能性があります。:)while ... case ... esac
-d

答え1

getoptit次のように2番目のスクリプト(私が保存したもの)を呼び出すとき:

getoptit -d -h

これにより、次のものが印刷されます。

MYSQL_HOST=''  MYSQL_USER=''  MYSQL_PASS=''  BACKUP_DIR='-h' Additionals: 

if [ ! "$BACKUP_DIR" ]; thenこのようにBACKUP_DIRが設定されていない場合は、テストしてみてください。すると、内部のコードが実行されないのが正常です。

各オプションが一度設定されたかどうかをテストするには、$ OPTAGRG値に基づいて割り当てる前にこれを行う必要があります。また、割り当てる前に $OPTARG が a で始まっていることを確認する必要があります'-'(-d -hエラーの場合)。

...
            d)
                    if [ ! -z "$BACKUP_DIR" ]; then
                            echo "backup dir already set"
                            exit 2
                    fi
                    if [ z"${OPTARG:0:1}" == "z-" ]; then
                            echo "backup dir starts with option string"
                            exit 2
                    fi
                    BACKUP_DIR=$OPTARG
                    ;;
...

答え2

他の答えは実際にあなたの質問に多くの答えを提供しないので、これは予想される動作であり、方法によってはPOSIX解釈中:

オプションにオプション引数が必要な場合、getoptsユーティリティはそれをシェル変数OPTARGに配置する必要があります。オプションがない場合、またはオプション・パラメーターなしでオプションが見つかった場合は、OPTARGをオフにする必要があります。 )

それが明示されているすべてであり、(長すぎない)物語を簡単に言うと、あなたはgetopts見えるオプションを魔法のように知らない完全に妥当な主張です。必要実際には、オプション引数ではなく別のオプションである引数があります。オプションに有効な引数-hで渡すのを防ぐ方法はありません-d。これは、実際にgetopts引数を必要とするオプションがコマンドラインの末尾に表示される場合にのみエラーが発生することを意味します。たとえば、次のようになります。

test.sh:

#!/bin/sh

while getopts ":xy:" o; do
    case "${o}" in
        :) echo "${OPTARG} requires an argument"; exit 1;
    esac
done

例:

$ ./test.sh -y -x
$

$ ./test.sh -x -y
y requires an argument

2番目のアプローチがうまくいかないのは、解析がgetoptsまだ同じであるため、ループに入ると「破損」がすでに完了しているためです。

この機能を絶対に無効にするには、Benubirdが指摘したように、オプション引数を直接確認し、有効なオプションと同じ場合はエラーが発生する必要があります。

答え3

引き続き使用する予定ですが、getopts後でいくつかの追加確認を行います。 (テストされていません)

errs=0
declare -A option=(
    [MYSQL_HOST]="-h"
    [MYSQL_USER]="-u"
    [MYSQL_PASS]="-p"
    [BACKUP_DIR]="-d" 
)
for var in "${!option[@]}"; do
    if [[ -z "${!var}" ]]; then
        echo "error: specify a value for $var with ${option[var]}"
        ((errs++))
    fi
done
((errs > 0)) && exit 1

Bash バージョン 4 が必要

答え4

まず使用する必要がありますif [ -z "$MYSQL_USER" ]

第二に、割り当ては必要ありません。if [ -z "$OPTARG" ]うまくいきます。

第三に、私はそれがあなたが本当に欲しいものだと思いますif [ ${#OPTARG} = 0 ]。 ${#x} は文字列 $x の長さを返す bash です。ここ)。

第四に、直接チェックする場合は、getoptsの代わりにgetoptを使用する方が柔軟性があるため、使用することをお勧めします。

最後に、最初の質問に答えるために、フラグのリストを一番上に配置して、フラグが引数として渡されるタイミングを検出できます。

args=( -h -u -p -d )

次に、指定された引数が次のオプションであることを確認するオプションを確認します。

d)
    BACKUP_DIR=$OPTARG
    if [ "$(echo ${args[@]/$OPTARG/})" = "$(echo ${args[@]})" ]
    then
        echo "Argument is an option! Error!"
    fi

完璧な答えではありませんが、効果があります!問題は、「-h」が完全に有効な引数であり、有効なフラグではなく引数を探しているという事実をシェルに知らせることができないことです。

関連情報