在 Bash 中使用 getopts 检索单个选项的多个参数

发布于 2024-12-06 07:22:43 字数 822 浏览 0 评论 0原文

我需要 getopts 方面的帮助。

我创建了一个 Bash 脚本,运行时如下所示:

$ foo.sh -i env -d directory -s subdirectory -f file

在处理每个标志的一个参数时它可以正常工作。但是,当我从每个标志调用多个参数时,我不确定如何从 getopts 中的变量中提取多个变量信息。

while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=$OPTARG;;
        f ) files=$OPTARG;;

     esac
done

获取选项后,我想从变量构建目录结构

foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3

那么目录结构将是有

/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3

什么想法吗?

I need help with getopts.

I created a Bash script which looks like this when run:

$ foo.sh -i env -d directory -s subdirectory -f file

It works correctly when handling one argument from each flag. But when I invoke several arguments from each flag I am not sure how to pull the multiple variable information out of the variables in getopts.

while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=$OPTARG;;
        f ) files=$OPTARG;;

     esac
done

After grabbing the options I then want to build directory structures from the variables

foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3

Then the directory structure would be

/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

巷子口的你 2024-12-13 07:22:43

您可以多次使用同一选项并将所有值添加到数组中

对于这里非常具体的原始问题,Ryan 的 mkdir -p 解决方案显然是最好的。

但是,对于使用 getopts 从同一选项获取多个值这一更普遍的问题,

#!/bin/bash

while getopts "m:" opt; do
    case $opt in
        m) multi+=("$OPTARG");;
        #...
    esac
done
shift $((OPTIND -1))

echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[@]}'"

echo "Or:"

for val in "${multi[@]}"; do
    echo " - $val"
done

输出为:

$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:

$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
 - one arg with spaces

$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
 - one
 - second argument
 - three

You can use the same option multiple times and add all values to an array.

For the very specific original question here, Ryan's mkdir -p solution is obviously the best.

However, for the more general question of getting multiple values from the same option with getopts, here it is:

#!/bin/bash

while getopts "m:" opt; do
    case $opt in
        m) multi+=("$OPTARG");;
        #...
    esac
done
shift $((OPTIND -1))

echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[@]}'"

echo "Or:"

for val in "${multi[@]}"; do
    echo " - $val"
done

The output would be:

$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:

$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
 - one arg with spaces

$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
 - one
 - second argument
 - three
椒妓 2024-12-13 07:22:43

我知道这个问题已经很老了,但我想把这个答案放在这里,以防有人来寻找答案。

像 BASH 这样的 shell 已经支持像这样递归地创建目录,所以实际上并不需要脚本。例如,原始海报想要类似的内容:

$ foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3

这可以使用此命令行轻松完成:

pong:~/tmp
[10] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/{file1,file2,file3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

或者甚至更短:

pong:~/tmp
[12] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

或者更短,更符合:

pong:~/tmp
[14] rmclean$ mkdir -pv test/directory/subdirectory{1,2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

或者最后,使用序列:

pong:~/tmp
[16] rmclean$ mkdir -pv test/directory/subdirectory{1..2}/file{1..3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

I know this question is old, but I wanted to throw this answer on here in case someone comes looking for an answer.

Shells like BASH support making directories recursively like this already, so a script isn't really needed. For instance, the original poster wanted something like:

$ foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3

This is easily done with this command line:

pong:~/tmp
[10] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/{file1,file2,file3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

Or even a bit shorter:

pong:~/tmp
[12] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

Or shorter, with more conformity:

pong:~/tmp
[14] rmclean$ mkdir -pv test/directory/subdirectory{1,2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’

Or lastly, using sequences:

pong:~/tmp
[16] rmclean$ mkdir -pv test/directory/subdirectory{1..2}/file{1..3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
恋你朝朝暮暮 2024-12-13 07:22:43

getopts 选项只能采用零个或一个参数。您可能想要更改界面以删除 -f 选项,然后迭代其余的非选项参数

usage: foo.sh -i end -d dir -s subdir file [...]

所以,

while getopts ":i:d:s:" opt; do
  case "$opt" in
    i) initial=$OPTARG ;;
    d) dir=$OPTARG ;;
    s) sub=$OPTARG ;;
  esac
done
shift $(( OPTIND - 1 ))

path="/$initial/$dir/$sub"
mkdir -p "$path"

for file in "$@"; do
  touch "$path/$file"
done

getopts options can only take zero or one argument. You might want to change your interface to remove the -f option, and just iterate over the remaining non-option arguments

usage: foo.sh -i end -d dir -s subdir file [...]

So,

while getopts ":i:d:s:" opt; do
  case "$opt" in
    i) initial=$OPTARG ;;
    d) dir=$OPTARG ;;
    s) sub=$OPTARG ;;
  esac
done
shift $(( OPTIND - 1 ))

path="/$initial/$dir/$sub"
mkdir -p "$path"

for file in "$@"; do
  touch "$path/$file"
done
柳若烟 2024-12-13 07:22:43

如果要为选项指定任意数量的值,可以使用简单的循环来查找它们并将它们填充到数组中。例如,让我们修改 OP 的示例以允许任意数量的 -s 参数:

unset -v sub
while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=("$OPTARG")
            until [[ $(eval "echo \${$OPTIND}") =~ ^-.* ]] || [ -z $(eval "echo \${$OPTIND}") ]; do
                sub+=($(eval "echo \${$OPTIND}"))
                OPTIND=$((OPTIND + 1))
            done
            ;;
        f ) files=$OPTARG;;
     esac
done

这采用第一个参数 ($OPTARG) 并将其放入数组 $sub 中。然后它将继续搜索剩余的参数,直到它命中另一个虚线参数或者没有更多的参数需要评估。如果它发现更多不是虚线参数的参数,则会将其添加到 $sub 数组中并增加 $OPTIND 变量。

因此,在OP的示例中,可以运行以下命令:

foo.sh -i test -d directory -s subdirectory1 subdirectory2 -f file1

如果我们将这些行添加到脚本中进行演示:

echo ${sub[@]}
echo ${sub[1]}
echo $files

输出将是:

subdirectory1 subdirectory2
subdirectory2
file1

If you want to specify any number of values for an option, you can use a simple loop to find them and stuff them into an array. For example, let's modify the OP's example to allow any number of -s parameters:

unset -v sub
while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=("$OPTARG")
            until [[ $(eval "echo \${$OPTIND}") =~ ^-.* ]] || [ -z $(eval "echo \${$OPTIND}") ]; do
                sub+=($(eval "echo \${$OPTIND}"))
                OPTIND=$((OPTIND + 1))
            done
            ;;
        f ) files=$OPTARG;;
     esac
done

This takes the first argument ($OPTARG) and puts it into the array $sub. Then it will continue searching through the remaining parameters until it either hits another dashed parameter OR there are no more arguments to evaluate. If it finds more parameters that aren't a dashed parameter, it adds it to the $sub array and bumps up the $OPTIND variable.

So in the OP's example, the following could be run:

foo.sh -i test -d directory -s subdirectory1 subdirectory2 -f file1

If we added these lines to the script to demonstrate:

echo ${sub[@]}
echo ${sub[1]}
echo $files

The output would be:

subdirectory1 subdirectory2
subdirectory2
file1
荒岛晴空 2024-12-13 07:22:43

我解决了与您遇到的同样的问题:

而不是:

foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3

这样做:

foo.sh -i test -d directory -s "subdirectory subdirectory2" -f "file1 file2 file3"

使用空格分隔符,您可以使用基本循环来运行它。
这是代码:

while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=$OPTARG;;
        f ) files=$OPTARG;;

     esac
done

for subdir in $sub;do
   for file in $files;do
      echo $subdir/$file
   done   
done

这是示例输出:

$ ./getopts.sh -s "testdir1 testdir2" -f "file1 file2 file3"
testdir1/file1
testdir1/file2
testdir1/file3
testdir2/file1
testdir2/file2
testdir2/file3

I fixed the same problem you had like this:

Instead of:

foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3

Do this:

foo.sh -i test -d directory -s "subdirectory subdirectory2" -f "file1 file2 file3"

With the space separator you can just run through it with a basic loop.
Here's the code:

while getopts ":i:d:s:f:" opt
   do
     case $opt in
        i ) initial=$OPTARG;;
        d ) dir=$OPTARG;;
        s ) sub=$OPTARG;;
        f ) files=$OPTARG;;

     esac
done

for subdir in $sub;do
   for file in $files;do
      echo $subdir/$file
   done   
done

Here's a sample output:

$ ./getopts.sh -s "testdir1 testdir2" -f "file1 file2 file3"
testdir1/file1
testdir1/file2
testdir1/file3
testdir2/file1
testdir2/file2
testdir2/file3
自演自醉 2024-12-13 07:22:43

实际上有一种方法可以使用 getopts 检索多个参数,但它需要使用 getopts' OPTIND 变量进行一些手动修改。

请参阅以下脚本(转载如下): https://gist.github.com/achalddave/290f7fcad89a0d7c3719。可能有一种更简单的方法,但这是我能找到的最快的方法。

#!/bin/sh

usage() {
cat << EOF
$0 -a <a1> <a2> <a3> [-b] <b1> [-c]
    -a      First flag; takes in 3 arguments
    -b      Second flag; takes in 1 argument
    -c      Third flag; takes in no arguments
EOF
}

is_flag() {
    # Check if $1 is a flag; e.g. "-b"
    [[ "$1" =~ -.* ]] && return 0 || return 1
}

# Note:
# For a, we fool getopts into thinking a doesn't take in an argument
# For b, we can just use getopts normal behavior to take in an argument
while getopts "ab:c" opt ; do
    case "${opt}" in
        a)
            # This is the tricky part.

            # $OPTIND has the index of the _next_ parameter; so "\${$((OPTIND))}"
            # will give us, e.g., ${2}. Use eval to get the value in ${2}.
            # The {} are needed in general for the possible case of multiple digits.

            eval "a1=\${$((OPTIND))}"
            eval "a2=\${$((OPTIND+1))}"
            eval "a3=\${$((OPTIND+2))}"

            # Note: We need to check that we're still in bounds, and that
            # a1,a2,a3 aren't flags. e.g.
            #   ./getopts-multiple.sh -a 1 2 -b
            # should error, and not set a3 to be -b.
            if [ $((OPTIND+2)) -gt $# ] || is_flag "$a1" || is_flag "$a2" || is_flag "$a3"
            then
                usage
                echo
                echo "-a requires 3 arguments!"
                exit
            fi

            echo "-a has arguments $a1, $a2, $a3"

            # "shift" getopts' index
            OPTIND=$((OPTIND+3))
            ;;
        b)
            # Can get the argument from getopts directly
            echo "-b has argument $OPTARG"
            ;;
        c)
            # No arguments, life goes on
            echo "-c"
            ;;
    esac
done

There actually is a way to retrieve multiple arguments using getopts, but it requires some manual hacking with getopts' OPTIND variable.

See the following script (reproduced below): https://gist.github.com/achalddave/290f7fcad89a0d7c3719. There's probably an easier way, but this was the quickest way I could find.

#!/bin/sh

usage() {
cat << EOF
$0 -a <a1> <a2> <a3> [-b] <b1> [-c]
    -a      First flag; takes in 3 arguments
    -b      Second flag; takes in 1 argument
    -c      Third flag; takes in no arguments
EOF
}

is_flag() {
    # Check if $1 is a flag; e.g. "-b"
    [[ "$1" =~ -.* ]] && return 0 || return 1
}

# Note:
# For a, we fool getopts into thinking a doesn't take in an argument
# For b, we can just use getopts normal behavior to take in an argument
while getopts "ab:c" opt ; do
    case "${opt}" in
        a)
            # This is the tricky part.

            # $OPTIND has the index of the _next_ parameter; so "\${$((OPTIND))}"
            # will give us, e.g., ${2}. Use eval to get the value in ${2}.
            # The {} are needed in general for the possible case of multiple digits.

            eval "a1=\${$((OPTIND))}"
            eval "a2=\${$((OPTIND+1))}"
            eval "a3=\${$((OPTIND+2))}"

            # Note: We need to check that we're still in bounds, and that
            # a1,a2,a3 aren't flags. e.g.
            #   ./getopts-multiple.sh -a 1 2 -b
            # should error, and not set a3 to be -b.
            if [ $((OPTIND+2)) -gt $# ] || is_flag "$a1" || is_flag "$a2" || is_flag "$a3"
            then
                usage
                echo
                echo "-a requires 3 arguments!"
                exit
            fi

            echo "-a has arguments $a1, $a2, $a3"

            # "shift" getopts' index
            OPTIND=$((OPTIND+3))
            ;;
        b)
            # Can get the argument from getopts directly
            echo "-b has argument $OPTARG"
            ;;
        c)
            # No arguments, life goes on
            echo "-c"
            ;;
    esac
done
烈酒灼喉 2024-12-13 07:22:43

最初的问题涉及 getopts,但还有另一种解决方案可以在没有 getopts 的情况下提供更灵活的功能(这可能有点冗长,但提供了更灵活的命令行界面)。下面是一个示例:

while [[ $# > 0 ]]
do
    key="$1"
    case $key in
        -f|--foo)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
                case $nextArg in
                    bar)
                        echo "--foo bar found!"
                    ;;
                    baz)
                        echo "--foo baz found!"
                    ;;
                    *)
                        echo "$key $nextArg found!"
                    ;;
                esac
                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        -b|--bar)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
                case $nextArg in
                    foo)
                        echo "--bar foo found!"
                    ;;
                    baz)
                        echo "--bar baz found!"
                    ;;
                    *)
                        echo "$key $nextArg found!"
                    ;;
                esac
                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        -z|--baz)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do

                echo "Doing some random task with $key $nextArg"

                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        *)
            echo "Unknown flag $key"
        ;;
    esac
    shift
done

在此示例中,我们循环遍历所有命令行选项,查找与我们接受的命令行标志(例如 -f 或 --foo)相匹配的参数。一旦找到一个标志,我们就会循环遍历每个参数,直到用完参数或遇到另一个标志。这让我们回到了只处理标志的外循环。

通过此设置,以下命令是等效的:

script -f foo bar baz
script -f foo -f bar -f baz
script --foo foo -f bar baz
script --foo foo bar -f baz

您还可以解析极其混乱的参数集,例如:

script -f baz derp --baz herp -z derp -b foo --foo bar -q llama --bar fight

要获取输出:

--foo baz found!
-f derp found!
Doing some random task with --baz herp
Doing some random task with -z derp
--bar foo found!
--foo bar found!
Unknown flag -q
Unknown flag llama
--bar fight found!

The original question deals with getopts, but there is another solution that provides more flexible functionality without getopts (this is perhaps a bit more verbose, but provides a far more flexible command line interface). Here is an example:

while [[ $# > 0 ]]
do
    key="$1"
    case $key in
        -f|--foo)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
                case $nextArg in
                    bar)
                        echo "--foo bar found!"
                    ;;
                    baz)
                        echo "--foo baz found!"
                    ;;
                    *)
                        echo "$key $nextArg found!"
                    ;;
                esac
                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        -b|--bar)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
                case $nextArg in
                    foo)
                        echo "--bar foo found!"
                    ;;
                    baz)
                        echo "--bar baz found!"
                    ;;
                    *)
                        echo "$key $nextArg found!"
                    ;;
                esac
                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        -z|--baz)
            nextArg="$2"
            while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do

                echo "Doing some random task with $key $nextArg"

                if ! [[ "$2" =~ -.* ]]; then
                    shift
                    nextArg="$2"
                else
                    shift
                    break
                fi
            done
        ;;
        *)
            echo "Unknown flag $key"
        ;;
    esac
    shift
done

In this example we are looping through all of the command line options looking for parameters that match our accepted command line flags (such as -f or --foo). Once we find a flag, we loop through every parameter until we run out of parameters or encounter another flag. This breaks us back out into our outer loop which only processes flags.

With this setup, the following commands are equivalent:

script -f foo bar baz
script -f foo -f bar -f baz
script --foo foo -f bar baz
script --foo foo bar -f baz

You can also parse incredibly disorganized parameter sets such as:

script -f baz derp --baz herp -z derp -b foo --foo bar -q llama --bar fight

To get the output:

--foo baz found!
-f derp found!
Doing some random task with --baz herp
Doing some random task with -z derp
--bar foo found!
--foo bar found!
Unknown flag -q
Unknown flag llama
--bar fight found!
一抹苦笑 2024-12-13 07:22:43
#!/bin/bash
myname=$(basename "$0")

# help function
help () { cat <<EOP
   $myname: -c cluster [...] -a action [...] -i instance [...]
EOP
}

# parse sub options
get_opts () {
  rs='' && rc=0 # return string and return code
  while [[ $# -gt 0 ]]; do
    shift
    [[ "$1" =~ -.* ]] && break ||  rs="$rs $1" && rc=$((rc + 1))
  done
  echo "$rs"
}

#parse entire command-line
while [[ $# -gt 0 ]]; do
    case $1 in
        "-a") ACTS="$(get_opts $@)"
           ;;
        "-i") INSTS=$(get_opts $@)
           ;;
        "-c") CLUSTERS=$(get_opts $@)
           ;;
        "-h") help
           ;;
        ?) echo "sorry, I dont do $1"
           exit
           ;;
    esac
    shift
done
#!/bin/bash
myname=$(basename "$0")

# help function
help () { cat <<EOP
   $myname: -c cluster [...] -a action [...] -i instance [...]
EOP
}

# parse sub options
get_opts () {
  rs='' && rc=0 # return string and return code
  while [[ $# -gt 0 ]]; do
    shift
    [[ "$1" =~ -.* ]] && break ||  rs="$rs $1" && rc=$((rc + 1))
  done
  echo "$rs"
}

#parse entire command-line
while [[ $# -gt 0 ]]; do
    case $1 in
        "-a") ACTS="$(get_opts $@)"
           ;;
        "-i") INSTS=$(get_opts $@)
           ;;
        "-c") CLUSTERS=$(get_opts $@)
           ;;
        "-h") help
           ;;
        ?) echo "sorry, I dont do $1"
           exit
           ;;
    esac
    shift
done
人生百味 2024-12-13 07:22:43

以下链接应该是此要求的通用解决方案。

它很容易注入,足够清晰易懂,也最大限度地减少了对原始代码的影响。

使用 getopts (bash) 的多个选项参数

function getopts-extra () {
    declare i=1
    # if the next argument is not an option, then append it to array OPTARG
    while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
        OPTARG[i]=${!OPTIND}
        let i++ OPTIND++
    done
}

# Use it within the context of `getopts`:
while getopts s: opt; do
    case $opt in
       s) getopts-extra "$@"
          args=( "${OPTARG[@]}" )
    esac
done

The following link should be a general solution for this requirement.

It's easy to inject and clear enough to understand also minimizes the impact on the original code.

Multiple option arguments using getopts (bash)

function getopts-extra () {
    declare i=1
    # if the next argument is not an option, then append it to array OPTARG
    while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
        OPTARG[i]=${!OPTIND}
        let i++ OPTIND++
    done
}

# Use it within the context of `getopts`:
while getopts s: opt; do
    case $opt in
       s) getopts-extra "$@"
          args=( "${OPTARG[@]}" )
    esac
done
别念他 2024-12-13 07:22:43

由于您没有显示您希望如何构建列表,

/test/directory/subdirectory/file1
. . .
test/directory/subdirectory2/file3

所以有点不清楚如何继续,但基本上您需要不断将任何新值附加到适当的变量,即

 case $opt in
    d ) dirList="${dirList} $OPTARG" ;;
 esac

请注意,在第一次传递时 dir 将为空,并且您将会在 ${dirList} 的最终值的起始位置出现一个空格。 (如果你确实需要不包含任何额外空格的代码,无论是前面还是后面,我可以向你展示一个命令,但它会很难理解,而且你似乎在这里不需要它,但让我知道)

然后您可以将列表变量包装在 for 循环中以发出所有值,即

for dir in ${dirList} do
   for f in ${fileList} ; do
      echo $dir/$f
   done
done

最后,将任何未知输入“捕获”到 case 语句被认为是很好的做法,即

 case $opt in
    i ) initial=$OPTARG;;
    d ) dir=$OPTARG;;
    s ) sub=$OPTARG;;
    f ) files=$OPTARG;;
    * ) 
       printf "unknown flag supplied "${OPTARG}\nUsageMessageGoesHere\n"
       exit 1
    ;;

 esac 

我希望这会有所帮助。

As you don't show how you hope to construct your list

/test/directory/subdirectory/file1
. . .
test/directory/subdirectory2/file3

it's a little unclear how to proceed, but basically you need to keep appending any new values to the appropriate variable, i.e.

 case $opt in
    d ) dirList="${dirList} $OPTARG" ;;
 esac

Note that on the first pass dir will be empty, and you'll wind up with a space leading at the from of your final value for ${dirList}. (If you really need code that doesn't include any extra spaces, front or back, there is a command I can show you, but it will be hard to understand, and it doesn't seem that you'll need it here, but let me know)

You can then wrap your list variables in for loops to emit all the values, i.e.

for dir in ${dirList} do
   for f in ${fileList} ; do
      echo $dir/$f
   done
done

Finally, it is considered good practice to 'trap' any unknown inputs to your case statement, i.e.

 case $opt in
    i ) initial=$OPTARG;;
    d ) dir=$OPTARG;;
    s ) sub=$OPTARG;;
    f ) files=$OPTARG;;
    * ) 
       printf "unknown flag supplied "${OPTARG}\nUsageMessageGoesHere\n"
       exit 1
    ;;

 esac 

I hope this helps.

如果没有你 2024-12-13 07:22:43

这是为单个选项传递多个参数的简单方法。

#!/bin/bash

#test.sh -i 'input1 input2'
#OR
#test.sh -i 'input*'

while getopts "i:" opt; do
    case $opt in
        i ) input=$OPTARG;;
    esac
done
inputs=( $input )

echo "First input is "$inputs""
echo "Second input is "${inputs[1]}""
echo "All inputs: "${inputs[@]}""

This is a simple way to pass multiple arguments for a single option.

#!/bin/bash

#test.sh -i 'input1 input2'
#OR
#test.sh -i 'input*'

while getopts "i:" opt; do
    case $opt in
        i ) input=$OPTARG;;
    esac
done
inputs=( $input )

echo "First input is "$inputs""
echo "Second input is "${inputs[1]}""
echo "All inputs: "${inputs[@]}""
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文