简体   繁体   中英

Unable to read bash shell script arguments

I am trying to read the arguments provided to a bash shell script like:

sh test.sh -s "xyz" -e staging --files file1 --tags tags

Below is my code:

options=$(getopt --long service:,environment:,help::,files::,tags:: -o s:e:h::f::t:: -- "$@")
eval set -- "$options"

for opt; do
    echo "$opt :: $2"
    case "$opt" in
        -h|--help) 
            echo "help"
            exit 0
            ;;
        -s|--service)
            service_name="$2"
            echo "Option chosen - service --> ${service_name}"
            shift
            ;;
        -e|--env)
            environment_name="$2"
            echo "Option chosen - environment --> ${environment_name}"
            shift
            ;;
        -t|--tags)
            tag_names="$2"
            echo "Option chosen - tags -> $tag_names"
            shift
            ;;
        -f|--files)
            filenames="$2"
            echo "Option chosen - files -> ${2}"
            shift
            ;;
        --)
            shift
            break;;
        \?)
            echo "Error: Invalid option"
            exit 0
            ;;
    esac
    shift
done

However, this is the result I am getting:

-s :: xyz
Option chosen - service --> xyz
xyz :: staging
-e :: --files
Option chosen - environment --> --files
staging :: --tags
--files :: 
Option chosen - files -> 
 :: file1
--tags :: tags
Option chosen - tags -> tags
 :: 
-- :: 

I was expecting something like:

-s :: xyz
Option chosen - service --> xyz
-e :: staging
Option chosen - environment --> staging
--files :: file1
Option chosen - files -> file1
--tags :: tags
Option chosen - tags -> tags

As you can see, the "environment" and "files" arguments are not correct.

I have tried with "shift 2" as well but still unable to get the correct results. Any idea what I am doing wrong?

First, most of the problem seems to be your doubled colons .

$: set -- -s "xyz" -e staging --files file1 --tags tags
$: echo "$@"
-s xyz -e staging --files file1 --tags tags
$: getopt --long service:,environment:,help::,files::,tags:: -o s:e:h::f::t:: -- "$@"
 -s 'xyz' -e 'staging' --files '' --tags '' -- 'file1' 'tags'
$: getopt --long service:,environment:,help:,files:,tags: -o s:e:h:f:t: -- "$@"
 -s 'xyz' -e 'staging' --files 'file1' --tags 'tags' --

Doubled colons mean a value is optional, but you have to bear in mind how that will be implemented. How will the engine know whether a following token is the argument to an option, or a different argument? Does it make any sense for --files to not have an argument?

c.f. this example of the model you are almost using.

You might wants getopts (with an s ) instead. There's a good example of that here .

In general, avoid optional arguments; if you need optional arguments, provide a default value, and allow a supplied option to override that value... but if the user types in the option flag, it should either require an argument, or not allow one. Consistent clarity of design is better than bugged flexibility. With a little more thought and code, you can get the best of both. Using the option should generally either be boolean, or a flag that there is a following argument to collect.

Here is much clear and easy way.

while getopts "f:e:b:" opt; do
    case $opt in
        e)  offsetto="$OPTARG"
            ;;
        f)  filename="$OPTARG"
            ;;
        b) offsetfrom="$OPTARG"
            ;;
    esac
done

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM