1

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?

  • My feeling is that you are misusing `getopts`. If you are processing the options in the way you are doing, I would remove the first two lines and just process them directly. I think that the `eval set` messes up your command line (Do a `echo "$@"` after the _eval_ to verify this). The usual approach to deal with _getopts_ is like [this](https://stackoverflow.com/questions/16483119/an-example-of-how-to-use-getopts-in-bash). – user1934428 Oct 06 '22 at 07:29
  • 1
    you're running your script as `sh`, which isn't `bash` – Fravadona Oct 06 '22 at 07:30
  • 1
    @user1934428 note that the question is using `getopt` (singular), not the more modern `getopts` (plural). `eval set -- "$options"` is a legacy solution for processing arguments in older shells where you can't build your own arrays and have to overwrite the positional arguments to get something safely iterable. – tjm3772 Oct 06 '22 at 15:23
  • 1
    @Fravadona : While it is true that the OP is not using bash, from what I see in this particular case, the difference between bash and sh shouldn't matter. – user1934428 Oct 07 '22 at 07:22
  • 1
    @user1934428 That's right, I just stated a fact. It doesn't matter for OP's current code so I didn't ask for the `bash` tag removal – Fravadona Oct 07 '22 at 08:05

2 Answers2

1

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.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
0

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
aze2201
  • 453
  • 5
  • 12