Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
howto:getopts_tutorial [2014/07/17 05:38]
ormaaj POSIX links
howto:getopts_tutorial [2018/03/21 00:07] (current)
ffox8 [See also]
Line 2: Line 2:
  
 {{keywords>​bash shell scripting arguments positional parameters options getopt getopts}} {{keywords>​bash shell scripting arguments positional parameters options getopt getopts}}
- 
-FIXME incomplete 
- 
-When you want to parse commandline arguments in a professional way, ''​getopts''​ is the tool of choice. Unlike its older brother ''​getopt''​ (note the missing //s//!), it's a shell builtin command. The advantage is 
-  * you don't need to hand your positional parameters through to an external program 
-  * ''​getopts''​ can easily set shell variables you can use for parsing (impossible for an //​external//​ process!) 
-  * you don't have to argue with several ''​getopt''​ implementations which had buggy concepts in the past (whitespaces,​ ...) 
-  * ''​getopts''​ is defined in POSIX(r) 
- 
-Some other methods to parse positional parameters (without ''​getopt(s)''​) are described in: [[scripting:​posparams | How to handle positional parameters]]. 
- 
-**Note that** ''​getopts''​ is not able to parse GNU-style long options (''<​nowiki>​--</​nowiki>​myoption''​) or XF86-style long options (''​-myoption''​)! 
  
 ===== Description ===== ===== Description =====
 +**Note that** ''​getopts''​ is neither able to parse GNU-style long options (''<​nowiki>​--</​nowiki>​myoption''​) nor XF86-style long options (''​-myoption''​). So, when you want to parse command line arguments in a professional ;-) way, ''​getopts''​ may or may not work for you. Unlike its older brother ''​getopt''​ (note the missing //s//!), it's a shell builtin command. The advantages are:
 +  * No need to pass the positional parameters through to an external program.
 +  * Being a builtin, ''​getopts''​ can set shell variables to use for parsing (impossible for an //​external//​ process!)
 +  * There'​s no need to argue with several ''​getopt''​ implementations which had buggy concepts in the past (whitespace,​ ...)
 +  * ''​getopts''​ is defined in POSIX(r).
  
 +----
  
 +Some other methods to parse positional parameters - using neither **getopt** nor **getopts** - are described in: [[scripting:​posparams | How to handle positional parameters]].
  
  
 ==== Terminology ==== ==== Terminology ====
  
-It's useful to know what we're talking about here, so let's see... Consider the following ​commandline:+It's useful to know what we're talking about here, so let's see... Consider the following ​command line:
 <​code>​ <​code>​
 mybackup -x -f /​etc/​mybackup.conf -r ./foo.txt ./bar.txt mybackup -x -f /​etc/​mybackup.conf -r ./foo.txt ./bar.txt
 </​code>​ </​code>​
-All these are positional parameters, but you can divide them into some logical groups: 
-  * ''​-x''​ is an **option**, a flag, a switch: **one** character, indroduced by a **dash** (''​-''​) 
-  * ''​-f''​ is also an option, but this option has an **additional argument** (argument to the option ''​-f''​):​ ''/​etc/​mybackup.conf''​. This argument is usually separated from its option (by a whitespace or any other splitting character) but that's not a must, ''​-f/​etc/​mybackup.conf''​ is valid. 
-  * ''​-r''​ depends on the configuration. In this example, ''​-r''​ doesn'​t take arguments, so it's a standalone option, like ''​-x''​ 
-  * ''​./​foo.txt''​ and ''​./​bar.txt''​ are remaining arguments without any option related. These often are used as **mass-arguments** (like for example the filenames you specify for ''​cp(1)''​) or for arguments that don't need an option to be recognized because of the intended behaviour of the program (like the filename argument you give your text-editor to open and display - why would one need an extra switch for that?). POSIX(r) calls them **operands**. 
  
-To give you an idea about why ''​getopts''​ is usefulThe above commandline could also read like...+These are all positional parameters, but they can be divided into several logical groups: 
 +  * ''​-x''​ is an **option** (aka **flag** or **switch**). It consists of a dash (''​-''​) followed by **one** character. 
 +  * ''​-f''​ is also an option, but this option has an associated **option argument** (an argument to the option ''​-f''​):​ ''/​etc/​mybackup.conf''​. The option argument is usually the argument following the option itself, but that isn't mandatory. Joining the option and option argument into a single argument ''​-f/​etc/​mybackup.conf''​ is valid. 
 +  * ''​-r''​ depends on the configuration. In this example, ''​-r''​ doesn'​t take arguments so it's a standalone option like ''​-x''​. 
 +  * ''​./​foo.txt''​ and ''​./​bar.txt''​ are remaining arguments without any associated options. These are often used as **mass-arguments**. For example, the filenames specified for ''​cp(1)'',​ or arguments that don't need an option to be recognized because of the intended behavior of the program. POSIX(r) calls them **operands**. 
 + 
 +To give you an idea about why ''​getopts''​ is usefulThe above command line is equivalent to:
 <​code>​ <​code>​
 mybackup -xrf /​etc/​mybackup.conf ./foo.txt ./bar.txt mybackup -xrf /​etc/​mybackup.conf ./foo.txt ./bar.txt
 </​code>​ </​code>​
-...which is very hard to parse by own code. ''​getopts''​ recognized all the common option formats. 
  
-The option flags can be **upper- and lowercase** characters, and of course **digits**. It may recognize other characters, but that's not recommended (usability and maybe problems with special characters).+which is complex to parse without the help of ''​getopts''​.
  
 +The option flags can be **upper- and lowercase** characters, or **digits**. It may recognize other characters, but that's not recommended (usability and maybe problems with special characters).
  
 ==== How it works ==== ==== How it works ====
  
-In general you need to call ''​getopts''​ several times. Each time it will use "the next" ​positional parameter ​(and a possible argument), if parsable, and provide it to you. ''​getopts''​ will not change the positional ​parameter set --- if you want to shift ityou have to do it manually ​after processing:+In general you need to call ''​getopts''​ several times. Each time it will use the next positional parameter and a possible argument, if parsable, and provide it to you. ''​getopts''​ will not change the set of positional ​parameters. If you want to shift them, it must be done manually: 
 <​code>​ <​code>​
-shift $(($OPTIND-1))+shift $((OPTIND-1))
 # now do something with $@ # now do something with $@
 </​code>​ </​code>​
  
-Since ''​getopts'' ​will set an exit status of //FALSE// when there'​s nothing left to parse, it's easy to use it in a while-loop:+Since ''​getopts'' ​sets an exit status of //FALSE// when there'​s nothing left to parse, it's easy to use in a while-loop: 
 <​code>​ <​code>​
 while getopts ...; do while getopts ...; do
Line 57: Line 54:
 </​code>​ </​code>​
  
-''​getopts''​ will parse options and their possible arguments. It will stop parsing on the first non-option argument (a string that doesn'​t begin with  a hyphen (''​-''​) that isn't an argument for any option ​infront ​of it). It will also stop parsing when it sees the ''<​nowiki>​--</​nowiki>''​ (double-hyphen),​ which means [[dict:​terms:​end_of_options | end of options]].+''​getopts''​ will parse options and their possible arguments. It will stop parsing on the first non-option argument (a string that doesn'​t begin with  a hyphen (''​-''​) that isn't an argument for any option ​in front of it). It will also stop parsing when it sees the ''<​nowiki>​--</​nowiki>''​ (double-hyphen),​ which means [[dict:​terms:​end_of_options | end of options]].
  
  
Line 65: Line 62:
  
 ^variable^description^ ^variable^description^
-|[[syntax:​shellvars#​OPTIND|OPTIND]]|Holds the index to the next argument to be processed. This is how ''​getopts''​ "​remembers"​ its own status between invocations. Also usefull ​to shift the positional parameters after processing with ''​getopts''​. ''​OPTIND''​ is initially set to 1, and **needs to be re-set to 1 if you want to parse anything again with getopts**|+|[[syntax:​shellvars#​OPTIND|OPTIND]]|Holds the index to the next argument to be processed. This is how ''​getopts''​ "​remembers"​ its own status between invocations. Also useful ​to shift the positional parameters after processing with ''​getopts''​. ''​OPTIND''​ is initially set to 1, and **needs to be re-set to 1 if you want to parse anything again with getopts**|
 |[[syntax:​shellvars#​OPTARG|OPTARG]]|This variable is set to any argument for an option found by ''​getopts''​. It also contains the option flag of an unknown option.| |[[syntax:​shellvars#​OPTARG|OPTARG]]|This variable is set to any argument for an option found by ''​getopts''​. It also contains the option flag of an unknown option.|
-|[[syntax:​shellvars#​OPTERR|OPTERR]]|(Values 0 or 1) Indicates if Bash should display error messages generated by the ''​getopts''​ builtin. The value is initialized to **1** on every shell startup - so be sure to always set it to **0** if you don't want to see annoying messages!|+|[[syntax:​shellvars#​OPTERR|OPTERR]]|(Values 0 or 1) Indicates if Bash should display error messages generated by the ''​getopts''​ builtin. The value is initialized to **1** on every shell startup - so be sure to always set it to **0** if you don't want to see annoying messages! ​**''​OPTERR''​ is not specified by POSIX for the ''​getopts''​ builtin utility --- only for the C ''​getopt()''​ function in ''​unistd.h''​ (''​opterr''​).** ''​OPTERR''​ is bash-specific and not supported by shells such as ksh93, mksh, zsh, or dash. |
  
 ''​getopts''​ also uses these variables for error reporting (they'​re set to value-combinations which arent possible in normal operation). ''​getopts''​ also uses these variables for error reporting (they'​re set to value-combinations which arent possible in normal operation).
- 
- 
- 
- 
- 
- 
- 
 ==== Specify what you want ==== ==== Specify what you want ====
  
Line 96: Line 86:
 <​code>​getopts fA:x VARNAME</​code>​ <​code>​getopts fA:x VARNAME</​code>​
  
-If the **very first character** of the option-string is a '':''​ (colon), which normally ​would be nonsense because there'​s no option letter ​preceeding ​it, ''​getopts''​ switches to the mode "​**silent error reporting**"​. In productive scripts, this is usually what you want (handle errors yourself ​and don't get disturbed by annoying messages).+If the **very first character** of the option-string is a '':''​ (colon), which would normally ​be nonsense because there'​s no option letter ​preceding ​it, ''​getopts''​ switches to "​**silent error reporting ​mode**". In productive scripts, this is usually what you want because it allows you to handle errors yourself ​without being disturbed by annoying messages.
  
 === Custom arguments to parse === === Custom arguments to parse ===
Line 105: Line 95:
  
 This way, you are able to parse any option set you like, here for example from an array: This way, you are able to parse any option set you like, here for example from an array:
 +
 <​code>​ <​code>​
 while getopts :f:h opt "​${MY_OWN_SET[@]}";​ do while getopts :f:h opt "​${MY_OWN_SET[@]}";​ do
Line 121: Line 112:
   * verbose mode   * verbose mode
   * silent mode   * silent mode
- 
  
 For productive scripts I recommend to use the silent mode, since everything looks more professional,​ when you don't see annoying standard messages. Also it's easier to handle, since the failure cases are indicated in an easier way. For productive scripts I recommend to use the silent mode, since everything looks more professional,​ when you don't see annoying standard messages. Also it's easier to handle, since the failure cases are indicated in an easier way.
Line 127: Line 117:
 === Verbose Mode === === Verbose Mode ===
  
-^invalid option|''​VARNAME''​ is set to ''?''​ (quersion-mark) and ''​OPTARG''​ is unset| +^invalid option|''​VARNAME''​ is set to ''?''​ (question-mark) and ''​OPTARG''​ is unset| 
-^required argument not found|''​VARNAME''​ is set to ''?''​ (quersion-mark), ''​OPTARG''​ is unset and an //error message is printed//|+^required argument not found|''​VARNAME''​ is set to ''?''​ (question-mark), ''​OPTARG''​ is unset and an //error message is printed//|
  
  
Line 135: Line 125:
 ^invalid option|''​VARNAME''​ is set to ''?''​ (question-mark) and ''​OPTARG''​ is set to the (invalid) option character| ^invalid option|''​VARNAME''​ is set to ''?''​ (question-mark) and ''​OPTARG''​ is set to the (invalid) option character|
 ^required argument not found|''​VARNAME''​ is set to '':''​ (colon) and ''​OPTARG''​ contains the option-character in question| ^required argument not found|''​VARNAME''​ is set to '':''​ (colon) and ''​OPTARG''​ contains the option-character in question|
- 
- 
  
 ===== Using it ===== ===== Using it =====
- 
- 
- 
- 
- 
- 
  
 ==== A first example ==== ==== A first example ====
Line 150: Line 132:
 Enough said - action! Enough said - action!
  
-Let's play with a very simple case: Only one option (''​-a''​) expected, without any arguments. Also we disable the //verbose error handling// by preceeding ​the whole option string with a colon ('':''​):​+Let's play with a very simple case: only one option (''​-a''​) expected, without any arguments. Also we disable the //verbose error handling// by preceding ​the whole option string with a colon ('':''​):​
 <code bash> <code bash>
 #!/bin/bash #!/bin/bash
Line 165: Line 147:
 done done
 </​code>​ </​code>​
 +
 I put that into a file named ''​go_test.sh'',​ which is the name you'll see below in the examples. I put that into a file named ''​go_test.sh'',​ which is the name you'll see below in the examples.
  
Line 170: Line 153:
  
 === Calling it without any arguments === === Calling it without any arguments ===
 +
 <​code>​ <​code>​
 $ ./​go_test.sh $ ./​go_test.sh
  
 </​code>​ </​code>​
-Nothing happened? Right. ''​getopts''​ didn't see any valid or invalid options (letters ​preceeded ​by a dash), so it wasn't triggered.+Nothing happened? Right. ''​getopts''​ didn't see any valid or invalid options (letters ​preceded ​by a dash), so it wasn't triggered.
  
 === Calling it with non-option arguments === === Calling it with non-option arguments ===
 +
 <​code>​ <​code>​
 $ ./​go_test.sh /etc/passwd $ ./​go_test.sh /etc/passwd
  
 </​code>​ </​code>​
-Again --- nothing happened. The **very same** case: ''​getopts''​ didn't see any valid or invalid options (letters ​preceeded ​by a dash), so it wasn't triggered.+Again --- nothing happened. The **very same** case: ''​getopts''​ didn't see any valid or invalid options (letters ​preceded ​by a dash), so it wasn't triggered.
  
 The arguments given to your script are of course accessible as ''​$1''​ - ''​${N}''​. The arguments given to your script are of course accessible as ''​$1''​ - ''​${N}''​.
  
 === Calling it with option-arguments === === Calling it with option-arguments ===
 +
 Now let's trigger ''​getopts'':​ Provide options. Now let's trigger ''​getopts'':​ Provide options.
  
Line 257: Line 243:
  
 === Calling it without any arguments === === Calling it without any arguments ===
 +
 <​code>​ <​code>​
 $ ./​go_test.sh $ ./​go_test.sh
  
 </​code>​ </​code>​
-As above, nothing ​happend. It wasn't triggered.+ 
 +As above, nothing ​happened. It wasn't triggered.
  
 === Calling it with non-option arguments === === Calling it with non-option arguments ===
 +
 <​code>​ <​code>​
 $ ./​go_test.sh /etc/passwd $ ./​go_test.sh /etc/passwd
  
 </​code>​ </​code>​
 +
 The **very same** case: It wasn't triggered. The **very same** case: It wasn't triggered.
  
Line 273: Line 263:
  
 **Invalid** option: **Invalid** option:
 +
 <​code>​ <​code>​
 $ ./​go_test.sh -b $ ./​go_test.sh -b
Line 278: Line 269:
  
 </​code>​ </​code>​
 +
 As expected, as above, ''​getopts''​ didn't accept this option and acted like programmed. As expected, as above, ''​getopts''​ didn't accept this option and acted like programmed.
  
 **Valid** option, but without the mandatory **argument**:​ **Valid** option, but without the mandatory **argument**:​
 +
 <​code>​ <​code>​
 $ ./​go_test.sh -a $ ./​go_test.sh -a
Line 286: Line 279:
  
 </​code>​ </​code>​
 +
 The option was okay, but there is an argument missing. The option was okay, but there is an argument missing.
  
 Let's provide **the argument**: Let's provide **the argument**:
 +
 <​code>​ <​code>​
 $ ./​go_test.sh -a /etc/passwd $ ./​go_test.sh -a /etc/passwd
Line 300: Line 295:
   * Internal: [[syntax:​ccmd:​while_loop]]   * Internal: [[syntax:​ccmd:​while_loop]]
   * POSIX [[http://​pubs.opengroup.org/​onlinepubs/​9699919799/​utilities/​getopts.html#​tag_20_54|getopts(1)]] and [[http://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​getopt.html|getopt(3)]]   * POSIX [[http://​pubs.opengroup.org/​onlinepubs/​9699919799/​utilities/​getopts.html#​tag_20_54|getopts(1)]] and [[http://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​getopt.html|getopt(3)]]
 +  * [[https://​stackoverflow.com/​questions/​192249/​how-do-i-parse-command-line-arguments-in-bash| parse CLI ARGV ]]
 +  * [[http://​mywiki.wooledge.org/​BashFAQ/​035|handle command-line arguments (options) to a script]]
 +
  • howto/getopts_tutorial.1405575522.txt
  • Last modified: 2014/07/17 05:38
  • by ormaaj