web-dev-qa-db-ja.com

getoptsと位置パラメータを混合することは可能ですか?

いくつかのスクリプトのラッパーとしてシェルスクリプトを設計したいと思います。 getoptsを使用してmyshell.shのパラメーターを指定し、指定したスクリプトに同じ順序で残りのパラメーターを渡します。

myshell.shが次のように実行される場合:

myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3

myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh

myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3

上記のすべてが次のように呼び出すことができるはずです

test.sh param1 param2 param3

オプションのパラメーターを利用することは可能ですか?myshell.shそして残りのパラメーターを基礎となるスクリプトに投稿しますか?

48
SiB

古いスレッドにコメントして申し訳ありませんが、これを行う方法を探していた私のように、私はそれらのために投稿すると思いました...

私はOPに似た何かをしたかったので、必要な関連情報を見つけました here および here

基本的に次のようなことをしたい場合:

script.sh [options] ARG1 ARG2

次に、このようなオプションを取得します。

while getopts "h:u:p:d:" flag; do
case "$flag" in
    h) HOSTNAME=$OPTARG;;
    u) USERNAME=$OPTARG;;
    p) PASSWORD=$OPTARG;;
    d) DATABASE=$OPTARG;;
esac
done

そして、次のような位置引数を取得できます。

ARG1=${@:$OPTIND:1}
ARG2=${@:$OPTIND+1:1}

詳細および詳細は、上記のリンクから入手できます。

お役に立てば幸いです!!

80
DRendar

Optsとargsを混在させる:

ARGS=""
echo "options :"
while [ $# -gt 0 ]
do
    unset OPTIND
    unset OPTARG
    while getopts as:c:  options
    do
    case $options in
            a)  echo "option a  no optarg"
                    ;;
            s)  serveur="$OPTARG"
                    echo "option s = $serveur"
                    ;;
            c)  cible="$OPTARG"
                    echo "option c = $cible"
                    ;;
        esac
   done
   shift $((OPTIND-1))
   ARGS="${ARGS} $1 "
   shift
done

echo "ARGS : $ARGS"
exit 1

結果:

bash test.sh  -a  arg1 arg2 -s serveur -c cible  arg3
options :
option a  no optarg
option s = serveur
option c = cible
ARGS :  arg1  arg2  arg3
11
chichi

getoptsは、param1オプションと-nオプションの組み合わせを解析しません。

Param1-3を他のオプションと同様にオプションに入れる方がはるかに良いです。

さらに、 shflags などの既存のライブラリを使用できます。かなりスマートで使いやすいです。

最後の方法は、getoptsを使用せずにparamsを解析する独自の関数を作成し、case構造を介してすべてのparamsを反復することです。それは最も難しい方法ですが、あなたの期待を正確に一致させる唯一の方法です。

3
rush

getoptsを拡張して、オプションと位置パラメーターを完全に混在させる方法を考えました。アイデアは、getoptsを呼び出すことと、見つかった位置パラメータをn1n2n3などに割り当てることです。

parse_args() {
    _parse_args 1 "$@"
}

_parse_args() {
    local n="$1"
    shift

    local options_func="$1"
    shift

    local OPTIND
    "$options_func" "$@"
    shift $(( OPTIND - 1 ))

    if [ $# -gt 0 ]; then
        eval test -n \${n$n+x}
        if [ $? -eq 0 ]; then
            eval n$n="\$1"
        fi

        shift
        _parse_args $(( n + 1 )) "$options_func" "$@"
    fi
}

次に、OPの場合、次のように使用できます。

main() {
    local n1='' n2='' n3=''
    local duration hostname script

    parse_args parse_main_options "$@"

    echo "n1 = $n1"
    echo "n2 = $n2"
    echo "n3 = $n3"
    echo "duration = $duration"
    echo "hostname = $hostname"
    echo "script   = $script"
}

parse_main_options() {
    while getopts d:h:s: opt; do
        case "$opt" in
            d) duration="$OPTARG" ;;
            h) hostname="$OPTARG" ;;
            s) script="$OPTARG"   ;;
        esac
    done
}

main "$@"

実行すると出力が表示されます:

$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
n1 = param1
n2 = param2
n3 = param3
duration = waittime
hostname = hostname
script   = test.sh

概念の証明にすぎませんが、誰かに役立つかもしれません。

注:parse_argsを使用する1つの関数がparse_argsを使用する別の関数を呼び出す場合、落とし穴がありますおよびlocal n4=''、ただし内部のものはそうではありませんおよび 4つ以上の位置パラメータが内部関数に渡されます

1
Michael Kropat

オプションと位置パラメータの混合物を簡単に処理するクイックキーをマッシュアップしました($ @に位置パラメータのみを残します)。

#!/bin/bash
while [ ${#} -gt 0 ];do OPTERR=0;OPTIND=1;getopts "p:o:hvu" arg;case "$arg" in
        p) echo "Path:   [$OPTARG]" ;;
        o) echo "Output: [$OPTARG]" ;;
        h) echo "Help"              ;;
        v) echo "Version"           ;;
    \?) SET+=("$1")                                           ;;
    *) echo "Coding error: '-$arg' is not handled by case">&2 ;;
esac;shift;[ "" != "$OPTARG" ] && shift;done
[ ${#SET[@]} -gt 0 ] && set "" "${SET[@]}" && shift

echo -e "=========\nLeftover (positional) parameters (count=$#) are:"
for i in `seq $#`;do echo -e "\t$i> [${!i}]";done

サンプル出力:

[root@hots:~]$ ./test.sh 'aa bb' -h -v -u -q 'cc dd' -p 'ee ff' 'gg hh' -o ooo
Help
Version
Coding error: '-u' is not handled by case
Path:   [ee ff]
Output: [ooo]
=========
Leftover (positional) parameters (count=4) are:
        1> [aa bb]
        2> [-q]
        3> [cc dd]
        4> [gg hh]
[root@hots:~]$
1
Vlad

getoptsを使用する代わりに、独自のbash引数パーサーを直接実装できます。これを実際の例として取り上げてください。名前と位置の引数を同時に処理できます。

#!/bin/bash

function parse_command_line() {
    local named_options;
    local parsed_positional_arguments;

    yes_to_all_questions="";
    parsed_positional_arguments=0;

    named_options=(
            "-y" "--yes"
            "-n" "--no"
            "-h" "--help"
            "-s" "--skip"
            "-v" "--version"
        );

    function validateduplicateoptions() {
        local item;
        local variabletoset;
        local namedargument;
        local argumentvalue;

        variabletoset="${1}";
        namedargument="${2}";
        argumentvalue="${3}";

        if [[ -z "${namedargument}" ]]; then
            printf "Error: Missing command line option for named argument '%s', got '%s'...\\n" "${variabletoset}" "${argumentvalue}";
            exit 1;
        fi;

        for item in "${named_options[@]}";
        do
            if [[ "${item}" == "${argumentvalue}" ]]; then
                printf "Warning: Named argument '%s' got possible invalid option '%s'...\\n" "${namedargument}" "${argumentvalue}";
                exit 1;
            fi;
        done;

        if [[ -n "${!variabletoset}" ]]; then
            printf "Warning: Overriding the named argument '%s=%s' with '%s'...\\n" "${namedargument}" "${!variabletoset}" "${argumentvalue}";
        else
            printf "Setting '%s' named argument '%s=%s'...\\n" "${thing_name}" "${namedargument}" "${argumentvalue}";
        fi;
        eval "${variabletoset}='${argumentvalue}'";
    }

    # https://stackoverflow.com/questions/2210349/test-whether-string-is-a-valid-integer
    function validateintegeroption() {
        local namedargument;
        local argumentvalue;

        namedargument="${1}";
        argumentvalue="${2}";

        if [[ -z "${2}" ]];
        then
            argumentvalue="${1}";
        fi;

        if [[ -n "$(printf "%s" "${argumentvalue}" | sed s/[0-9]//g)" ]];
        then
            if [[ -z "${2}" ]];
            then
                printf "Error: The %s positional argument requires a integer, but it got '%s'...\\n" "${parsed_positional_arguments}" "${argumentvalue}";
            else
                printf "Error: The named argument '%s' requires a integer, but it got '%s'...\\n" "${namedargument}" "${argumentvalue}";
            fi;
            exit 1;
        fi;
    }

    function validateposisionaloption() {
        local variabletoset;
        local argumentvalue;

        variabletoset="${1}";
        argumentvalue="${2}";

        if [[ -n "${!variabletoset}" ]]; then
            printf "Warning: Overriding the %s positional argument '%s=%s' with '%s'...\\n" "${parsed_positional_arguments}" "${variabletoset}" "${!variabletoset}" "${argumentvalue}";
        else
            printf "Setting the %s positional argument '%s=%s'...\\n" "${parsed_positional_arguments}" "${variabletoset}" "${argumentvalue}";
        fi;
        eval "${variabletoset}='${argumentvalue}'";
    }

    while [[ "${#}" -gt 0 ]];
    do
        case ${1} in
            -y|--yes)
                yes_to_all_questions="${1}";
                printf "Named argument '%s' for yes to all questions was triggered.\\n" "${1}";
                ;;

            -n|--no)
                yes_to_all_questions="${1}";
                printf "Named argument '%s' for no to all questions was triggered.\\n" "${1}";
                ;;

            -h|--help)
                printf "Print help here\\n";
                exit 0;
                ;;

            -s|--skip)
                validateintegeroption "${1}" "${2}";
                validateduplicateoptions g_installation_model_skip_commands "${1}" "${2}";
                shift;
                ;;

            -v|--version)
                validateduplicateoptions branch_or_tag "${1}" "${2}";
                shift;
                ;;

            *)
                parsed_positional_arguments=$((parsed_positional_arguments+1));

                case ${parsed_positional_arguments} in
                    1)
                        validateposisionaloption branch_or_tag "${1}";
                        ;;

                    2)
                        validateintegeroption "${1}";
                        validateposisionaloption g_installation_model_skip_commands "${1}";
                        ;;

                    *)
                        printf "ERROR: Extra positional command line argument '%s' found.\\n" "${1}";
                        exit 1;
                        ;;
                esac;
                ;;
        esac;
        shift;
    done;

    if [[ -z "${g_installation_model_skip_commands}" ]];
    then
        g_installation_model_skip_commands="0";
    fi;
}

この関数は次のように呼び出します。

#!/bin/bash
source ./function_file.sh;
parse_command_line "${@}";

使用例:

./test.sh as 22 -s 3
Setting the 1 positional argument 'branch_or_tag=as'...
Setting the 2 positional argument 'skip_commands=22'...
Warning: Overriding the named argument '-s=22' with '3'...

参照:

  1. example_installation_model.sh.md
  2. 引数の正しい数を確認する
  3. https://unix.stackexchange.com/questions/129391/passing-named-arguments-to-Shell-scripts
  4. bashでgetoptsを使用する方法の例
0
user