LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-26-2009, 08:14 PM   #1
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Rep: Reputation: 16
Question getopts script


At the moment I have a fallowing script:
Code:
#!/bin/bash

INTERFACE="$*"
IP="$1"
NETMASK="$2"
MTU="$3"

while getopts ":i:" x; do
  case $x in
    i)
      echo "$OPTARG"
      ;;
    \?)
      echo "Invalid option: -$OPTARG"
      ;;
    :)
      echo "Option -$OPTARG requires an argument."
      ;;
  esac
done

echo INTERFACE: $*
echo IP: $IP
echo NETMASK: $NETMASK
echo MTU: $MTU
If I use it like this:
./script.sh -i eth0 192.168.1.1 255.255.255.0 1500

the output is like this:

Code:
eth0
INTERFACE: -i eth0 192.168.1.1 255.255.255.0 1500
IP: -i
NETMASK: eth0
MTU: 192.168.1.1

How to achieve the fallowing output using "-i interface" wherever I like:
Code:
INTERFACE: eth0
IP: 192.168.1.1
NETMASK: 255.255.255.0
MTU: 1500
I mean ./script.sh -i eth0 192.168.1.1 255.255.255.0 1500 and ./script.sh 192.168.1.1 -i eth0 255.255.255.0 1500 and ./script.sh 192.168.1.1 255.255.255.0 1500 -i eth0 etc should all give the same output:
Code:
INTERFACE: eth0
IP: 192.168.1.1
NETMASK: 255.255.255.0
MTU: 1500
Any ideas how to achieve this?
 
Old 11-26-2009, 09:18 PM   #2
gratuitous_arp
LQ Newbie
 
Registered: Jul 2009
Posts: 28

Rep: Reputation: 17
Take a look at the example scripts included with the getopt package:

/usr/share/doc/util-linux/examples/
 
Old 11-27-2009, 12:42 AM   #3
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
AFAIK getopts does not support arguments presented before the last option. This implements a long-standing convention, in use for more than 40 years.

So the only listed command line form that getopts will handle is
Code:
./script.sh -i eth0 192.168.1.1 255.255.255.0 1500
You are, of course, free to code whatever you like but a) the toolsets are written according to convention and b) users are programmed (!) to convention.

Last edited by catkin; 11-27-2009 at 12:42 AM. Reason: punctuation
 
Old 11-27-2009, 01:43 AM   #4
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Original Poster
Rep: Reputation: 16
Quote:
Originally Posted by catkin View Post
AFAIK getopts does not support arguments presented before the last option. This implements a long-standing convention, in use for more than 40 years.
You mean getopts doesn't support any not-getopts arguments placed before '-i eth0'? However, is there a possibility to stop getopts looking for options? I know this a silly example, but lets say:
./script.sh -i eth0 192.168.1.1 255.255.255.0 -1500

How to achieve that getopts doesn't take '-1500' as an option? In other words getopts should stop it's work after './script.sh -i eth0'
 
Old 11-27-2009, 02:41 AM   #5
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by m4rtin View Post
You mean getopts doesn't support any not-getopts arguments placed before '-i eth0'? However, is there a possibility to stop getopts looking for options? I know this a silly example, but lets say:
./script.sh -i eth0 192.168.1.1 255.255.255.0 -1500

How to achieve that getopts doesn't take '-1500' as an option? In other words getopts should stop it's work after './script.sh -i eth0'
getopts keeps processing options (and any associated option arguments) according to optstring until it encounters something which is not described by optstring.

In your example, assuming opstring is "i:" (note: no leading ":"), getopts would stop processing on finding 192.168.1.1. The usual idiom after getopts completes is to run shift $(( $OPTIND-1 )) leaving the command line words from 192.168.1.1 onward in $1, $2, $3 ...

Another convention, where non-option arguments may begin with "-" is to allow "-" as a special option to indicate the end of options (I have not ever needed to implement this). In the example you give it is not necessary because 192.168.1.1 terminates the options but it would be necessary, for example, if the command line were ./script.sh -i eth0 -1500 192.168.1.1 255.255.255.0 which would otherwise result in an invalid option "5".
 
Old 12-07-2009, 10:00 AM   #6
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Original Poster
Rep: Reputation: 16
thanks for informative replies! However, one more question. How to handle negative flag values using getopts? For example:

./script.sh -i -eth0 192.168.1.1 255.255.255.0 1500

Where '-eth0' is a value for option '-i'. Is it possible at all?
 
Old 12-07-2009, 11:12 AM   #7
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by m4rtin View Post
How to handle negative flag values using getopts? For example:

./script.sh -i -eth0 192.168.1.1 255.255.255.0 1500

Where '-eth0' is a value for option '-i'. Is it possible at all?
I just tried it and it worked OK using this generic getopts script (intended as a proforma for use in a function -- hence the indentation and comments with no following code)
Code:
#!/bin/bash

    # Set defaults that may be overriden by command line parsing
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Parse command line
    # ~~~~~~~~~~~~~~~~~~
    emsg=''
    while getopts dhVo: opt 2>/dev/null
    do
        case $opt in
            d )
                debug='YES'
                ;;
            h )
                usage -v
                \exit 0
                ;;
            V )
                echo "$prgnam version $prg_ver"
                \exit 0
                ;;
            o )
                o_optarg="$OPTARG"
                ;;
            * )
                emsg="${lf}Invalid option '$opt'"
        esac
    done

    # Test for extra arguments
    # ~~~~~~~~~~~~~~~~~~~~~~~~
    shift $(( $OPTIND-1 ))
    if [[ $* != '' ]]; then
        emsg="${lf}Invalid extra argument(s) '$*'"
    fi

    # Test for mandatory options not set
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if [[ ${o_optarg:-} = '' ]]; then
        emsg="${lf}Mandatory option -o not given"
    fi

    # Report any errors
    # ~~~~~~~~~~~~~~~~~
    if [[ $emsg != '' ]]; then
        echo "$emsg" >&2
        usage
        \exit 1
    fi

    echo "DEBUG: o_optarg is '$o_optarg'"
Here's the call and output
Code:
c:~/d/bin/try$ ./getopts.sh -o -eth0
DEBUG: o_optarg is '-eth0'
 
Old 12-13-2009, 07:25 PM   #8
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Original Poster
Rep: Reputation: 16
catkin, thanks for your script! I made a fallowing one:

Code:
#!/bin/bash

while getopts "r:i:" opt; do
  case $opt in
        r)
        ROUTER="$OPTARG"
        ;;
        i)
        IFNAME="$OPTARG"
        ;;
        *)
        echo "Invalid option: -$OPTARG"
        ;;
  esac
done

shift $(( $OPTIND-1 ))

IP="$1"
SM="$2"

echo $ROUTER
echo $IFNAME
echo $IP
echo $SM

This works almost as I wish. It doesn't care order of getopts flags and handles negative values just fine:
Code:
./getopts -r router_name -i eth0 192.168.1.1 24
router_name
eth0
192.168.1.1
24
Code:
./getopts -i -eth0 -r router_name 192.168.1.1 -24
router_name
-eth0
192.168.1.1
-24
However, if I do for example:

./getopts -r router_name 192.168.1.1 24 -i eth0

I get:
Code:
router_name

192.168.1.1
24
Is it somehow possible to make the position of getopts flags independent? In other words fallowing flag positions should work:

./getopts -r router_name 192.168.1.1 24 -i eth0
./getopts 192.168.1.1 24 -i eth0 -r router_name
etc

How to accomplish this?
 
Old 12-13-2009, 10:03 PM   #9
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
That's not possible with getopts, AFAIK. getopts reads the parameters sequentially expecting to find only flags that start with "-" and the optional arguments that follow them. Any other entries that don't follow this pattern would confuse the $OPTARG and $OPTIND variables.

The way it works now also makes it easily possible to have arguments that start with "-", since otherwise they'd be seen as unrecognized options.

Just about all other programs out there follow this basic pattern as well.

I suppose it'd be possible to manually process the line and extract the address before applying getopts, but that would probably be much more trouble than its worth. I suggest either giving the ip number its own flag, or simply leaving it as-is.
 
Old 12-14-2009, 01:38 AM   #10
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Original Poster
Rep: Reputation: 16
Quote:
Originally Posted by David the H. View Post
That's not possible with getopts, AFAIK. getopts reads the parameters sequentially expecting to find only flags that start with "-" and the optional arguments that follow them. Any other entries that don't follow this pattern would confuse the $OPTARG and $OPTIND variables.

The way it works now also makes it easily possible to have arguments that start with "-", since otherwise they'd be seen as unrecognized options.

Just about all other programs out there follow this basic pattern as well.

I suppose it'd be possible to manually process the line and extract the address before applying getopts, but that would probably be much more trouble than its worth. I suggest either giving the ip number its own flag, or simply leaving it as-is.
ok, I see. Thanks for explanation! However, if somebody has an example with manual execution string processing which allows similar:
./getopts -r router_name 192.168.1.1 24 -i eth0

script executions, then I'll happily research those On the other hand, if it's very complicated and may bring along some errors, I'll leave it as it is.

Last edited by m4rtin; 12-14-2009 at 01:43 AM.
 
Old 12-14-2009, 03:37 AM   #11
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
It might be possible by incrementing OPTIND but you would be using an undocumented feature that might not be supported in a later version of bash; if you're OK with that and don't mind maybe having to fix your script (or someone else maybe having to fix your script) later.

A better approach might be to use -r router_name:192.168.1.1 24 on the command line. That way $OPTARG would be "router_name:192.168.1.1 24: and you could parse the components by
Code:
name="${OPTARG%:*}"
ip="${OPTARG#*:}"
 
Old 12-14-2009, 06:18 AM   #12
m4rtin
Member
 
Registered: Sep 2007
Posts: 261

Original Poster
Rep: Reputation: 16
Quote:
Originally Posted by catkin View Post
It might be possible by incrementing OPTIND but you would be using an undocumented feature that might not be supported in a later version of bash; if you're OK with that and don't mind maybe having to fix your script (or someone else maybe having to fix your script) later.

A better approach might be to use -r router_name:192.168.1.1 24 on the command line. That way $OPTARG would be "router_name:192.168.1.1 24: and you could parse the components by
Code:
name="${OPTARG%:*}"
ip="${OPTARG#*:}"
I see. However, it would be nice if the syntax of the execution line stays the same(and I don't mind repairing the script if it doesn't work in later versions of bash):

./getopts -r router_name 192.168.1.1 24 -i eth0

As I understand, OPTIND holds the index to the next argument to be processed. Default value for OPTIND should be 1. As I understand, every time that getopts finds a argument the value of the OPTIND is incremented by one? Now how does it help me if I increment OPTIND values manually?
 
Old 12-14-2009, 08:47 AM   #13
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by m4rtin View Post
As I understand, OPTIND holds the index to the next argument to be processed. Default value for OPTIND should be 1. As I understand, every time that getopts finds a argument the value of the OPTIND is incremented by one? Now how does it help me if I increment OPTIND values manually?
It is possible that getopts will use the changed OPTIND when choosing the next $1, $2, $3 ... $n to examine for one of the options specified by "r:i:". You could experiment.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
getopts kj6loh Programming 4 09-10-2009 03:53 PM
[SOLVED] bash : getopts problem in bash script. angel115 Programming 2 03-02-2009 10:53 AM
question on getopts tostay2003 Programming 1 12-25-2007 04:39 AM
struggling with getopts mike9287 Programming 6 07-14-2006 12:42 AM
Help with getopts command Rezon Programming 3 10-22-2003 04:12 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:35 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration