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
Last revision Both sides next revision
scripting:posparams [2011/06/04 11:11]
thebonsai typo
scripting:posparams [2015/10/20 00:49]
anthony additional info for using set
Line 6: Line 6:
 ===== Intro ===== ===== Intro =====
  
-The day will come when you want to give arguments to your scripts. These arguments are reflected ​as the **positional parameters** ​inside your scriptMost relevant special parameters are described below:+The day will come when you want to give arguments to your scripts. These arguments are known as **positional parameters**. ​Some relevant special parameters are described below:
 ^Parameter(s)^Description^ ^Parameter(s)^Description^
 |''​$0''​|the first positional parameter, equivalent to ''​argv[0]''​ in C, see [[scripting:​posparams#​the_first_argument | the first argument]]| |''​$0''​|the first positional parameter, equivalent to ''​argv[0]''​ in C, see [[scripting:​posparams#​the_first_argument | the first argument]]|
Line 17: Line 17:
  
  
-These positional parameters reflect exactly what was given to the script when it was called. ​There are no special things interpreted: ​Option-switch parsing (''​-h''​ for displaying help) is not done in this stage.+These positional parameters reflect exactly what was given to the 
 +script when it was called. 
 + 
 +Option-switch parsing (e.g. ''​-h''​ for displaying help) is not performed at 
 +this point.
  
 See also [[dict:​terms:​parameter | the dictionary entry for "​parameter"​]]. See also [[dict:​terms:​parameter | the dictionary entry for "​parameter"​]].
Line 24: Line 28:
 ===== The first argument ===== ===== The first argument =====
  
-The very first argument you can access is referenced ​by ''​$0''​. It usually ​is set to the script'​s name exactly ​like it was called, and it's set on shell initialization:​+The very first argument you can access is referenced ​as ''​$0''​. It 
 +is usually set to the script'​s name exactly ​as called, and it's 
 +set on shell initialization:​
  
 __Testscript__ - it just echos ''​$0'':​ __Testscript__ - it just echos ''​$0'':​
Line 31: Line 37:
 echo "​$0"​ echo "​$0"​
 </​code>​ </​code>​
-You see, ''​$0''​ is always set to however you call that script (''​$''​ is the prompt...):+You see, ''​$0''​ is always set to the name the script ​is called with (''​$''​ is the prompt...):
 <​code>​ <​code>​
 > ./​testscript ​ > ./​testscript ​
Line 47: Line 53:
 </​code>​ </​code>​
  
 +In other terms, ''​$0''​ is not a positional parameter, it's a
 +special parameter independent from the positional parameter list. It
 +can be set to anything. In the **ideal** case it's the pathname
 +of the script, but since this gets set on invocation, the invoking
 +program can easily influence it (the ''​login''​ program does that for
 +login shells, by prefixing a dash, for example).
  
-Also, to be over-exact: ''​$0''​ is not a positional parameter, it's a special parameter independent from the real parameter list. Also it really can be set to anything. In the **ideal** case it's the pathname of the script, but since this is set on invocation, the invoking program can easily influence it (the ''​login''​ program does that for login shells, by prepending a dash, for example). +Inside a function, ''​$0''​ still behaves as described ​above. To 
- +get the function name, use ''​$FUNCNAME''​.
-Inside a function, ''​$o''​ still reflects what was told above. To get the function name, use ''​$FUNCNAME''​. +
  
 ===== Shifting ===== ===== Shifting =====
Line 62: Line 72:
   * in general: ''​$N''​ will become ''​$N-1''​   * in general: ''​$N''​ will become ''​$N-1''​
  
-The command can take a number as argument: ​How many positions to shift. +The command can take a number as argument: ​Number of positions to shift. 
- So, a ''​shift 4'' ​will shift ''​$5''​ to ''​$1''​.+ e.g. ''​shift 4'' ​shifts ​''​$5''​ to ''​$1''​.
  
 ===== Using them ===== ===== Using them =====
Line 81: Line 91:
 echo "​Argument 5: $5" echo "​Argument 5: $5"
 </​code>​ </​code>​
-Well, it might be useful in one or the other situation, ​but this way is not very flexibleYou're fixed in your maximum number of arguments - which is a bad idea if you write a script that takes many filenames as arguments.+ 
 +While useful in another ​situation, this way is lacks flexibility. 
 +The maximum number of arguments ​is a fixedvalue 
 +- which is a bad idea if you write a script that takes many filenames 
 +as arguments.
  
 => forget that one => forget that one
  
-==== Loopings ​====+==== Loops ====
  
 There are several ways to loop through the positional parameters. There are several ways to loop through the positional parameters.
Line 91: Line 105:
 ---- ----
  
-You can code a [[syntax:​ccmd:​c_for | C-style for-loop]] using ''​$#''​ as end-value. On every iteration, the ''​shift''​-command is used to shift the argument list:+You can code a [[syntax:​ccmd:​c_for | C-style for-loop]] using ''​$#''​ 
 +as the end value. On every iteration, the ''​shift''​-command is used to 
 +shift the argument list: 
 <​code>​ <​code>​
 numargs=$# numargs=$#
-for ((i=1 ; i <= numargs ; i++))do +for ((i=1 ; i <= numargs ; i++)) 
-  echo "​$1"​ +do 
-  shift+    echo "​$1"​ 
 +    shift
 done done
 </​code>​ </​code>​
-Not very stylish, but okay, usable. The ''​numargs''​ variable is used to store the initial value of ''​$#''​ because ​it will change ​due to the shifting.+ 
 +Not very stylish, but usable. The ''​numargs''​ variable is used 
 +to store the initial value of ''​$#''​ because ​the shift command 
 +will change ​it as the script runs.
  
 ---- ----
  
-Another way to iterate one-by-one ​is the ''​for''​-loop without given wordlist, it will use the positional parameters as wordlist ​then:+Another way to iterate one argument at a time is the ''​for''​ loop 
 +without ​given wordlist. The loop uses the positional parameters as wordlist: 
 <​code>​ <​code>​
-for argdo +for arg 
-  echo "​$arg"​+do 
 +    echo "​$arg"​
 done done
 </​code>​ </​code>​
-__Advantage:​__ The positional parameters will be preserved ​and not shifted into nirvana!+__Advantage:​__ The positional parameters will be preserved
  
 ---- ----
  
-The next way is similar to the first example (the ''​for''​-loop), but it doesn'​t test for reaching ''​$#''​. It shifts and checks if ''​$1''​ still expands to something, using the [[commands:​classictest | test command]]:+The next method ​is similar to the first example (the ''​for''​ loop), but 
 +it doesn'​t test for reaching ''​$#''​. It shifts and checks if ''​$1''​ 
 +still expands to something, using the [[commands:​classictest | test command]]: 
 <​code>​ <​code>​
-while [ "​$1"​ ]do +while [ "​$1"​ ] 
-  echo "​$1"​ +do 
-  shift+    echo "​$1"​ 
 +    shift
 done done
 </​code>​ </​code>​
-Looks nice, but it has the disadvantage ​to stop when ''​$1''​ is empty (null-string). Let's modify it to run as long as ''​$1''​ is defined (but may be empty), using [[syntax:​pe#​use_an_alternate_value | parameter expansion for an alternate value]]:+ 
 +Looks nice, but has the disadvantage ​of stopping ​when ''​$1''​ is empty 
 +(null-string). Let's modify it to run as long as ''​$1''​ is defined 
 +(but may be null), using [[syntax:​pe#​use_an_alternate_value | parameter expansion for an alternate value]]: 
 <​code>​ <​code>​
 while [ "​${1+defined}"​ ]; do while [ "​${1+defined}"​ ]; do
Line 127: Line 159:
 done done
 </​code>​ </​code>​
- 
- 
  
 ==== Getopts ==== ==== Getopts ====
Line 138: Line 168:
 ==== All Positional Parameters ==== ==== All Positional Parameters ====
  
-Sometimes it's necessary to just "​relay"​ or "hand through" given arguments to another program. It's very inefficient to do that in one of these loops, ​also you will destroy integrity, most likely (spaces!).+Sometimes it's necessary to just "​relay"​ or "pass" given 
 +arguments to another program. It's very inefficient to do that in one 
 +of these loops, ​as you will destroy integrity, most likely 
 +(spaces!).
  
-The shell-developers ​invented ​''​$*''​ and ''​$@''​ for this purpose.+The shell developers ​created ​''​$*''​ and ''​$@''​ for this purpose.
  
-As overwiew:+As overview:
  
 ^Syntax ​     ^Effective result ​                ^ ^Syntax ​     ^Effective result ​                ^
Line 150: Line 183:
 |  ''"​$@"'' ​ |  ''"​$1"​ "​$2"​ "​$3"​ ... "​${N}"'' ​ | |  ''"​$@"'' ​ |  ''"​$1"​ "​$2"​ "​$3"​ ... "​${N}"'' ​ |
  
-You see that without ​being quoted (double-quoted), both have the same effect: All positional parameters from ''​$1''​ to the last used one are expanded without any specials. A subsequent wordsplitting will recognize as much words as expanded before (i.e. it "​doesn'​t preserve words"​).+Without ​being quoted (double ​quotes), both have the same 
 +effect: All positional parameters from ''​$1''​ to the last one used are 
 +expanded without any special handling.
  
-When the ''​$*''​ special parameter is doublequoted, it expands to the equivalent of: ''"​$1c$2c$3c$4c........$N"'',​ where '​c'​ is the first character of ''​IFS''​.+When the ''​$*''​ special parameter is double quoted, it expands to the 
 +equivalent of: ''"​$1c$2c$3c$4c........$N"'',​ where '​c'​ is the first 
 +character of ''​IFS''​.
  
-But when the ''​$@''​ special parameter is used inside ​doublequotes, it expands to the equivanent of...+But when the ''​$@''​ special parameter is used inside ​double quotes, it 
 +expands to the equivanent of...
  
 ''"​$1"​ "​$2"​ "​$3"​ "​$4"​ ..... "​$N"''​ ''"​$1"​ "​$2"​ "​$3"​ "​$4"​ ..... "​$N"''​
  
-...which **exactly ​reflects all positional parameters ​like they were initially ​set** and given to the script or the function. If you want to re-use your positional parameters to **call another program** (for example in a wrapper-script),​ then this is the choice for you, use the doublequoted ​''"​$@"''​.+...which **reflects all positional parameters ​as they were 
 +set initially** and passed ​to the script or function. If you want 
 +to re-use your positional parameters to **call another program** (for 
 +example in a wrapper-script),​ then this is the choice for you, use 
 +double quoted ​''"​$@"''​.
  
 Well, let's just say: **You almost always want a quoted ''"​$@"''​!** Well, let's just say: **You almost always want a quoted ''"​$@"''​!**
Line 164: Line 206:
 ==== Range Of Positional Parameters ==== ==== Range Of Positional Parameters ====
  
-Another way to mass-expand the positional parameters is similar to what is possible for a range of characters using the [[syntax:​pe#​substring_expansion | substring expansion]] on normal parameters and the range mass expansion of [[syntax:​arrays | arrays]].+Another way to mass expand the positional parameters is similar to 
 +what is possible for a range of characters using 
 +[[syntax:​pe#​substring_expansion | substring expansion]] on normal 
 +parameters and the mass expansion ​range of [[syntax:​arrays | arrays]].
  
 ''​${@:​START:​COUNT}''​ ''​${@:​START:​COUNT}''​
Line 174: Line 219:
 ''"​${*:​START:​COUNT}"''​ ''"​${*:​START:​COUNT}"''​
  
-The rules for using ''​@''​ or ''​*''​ and the quoting are the same as above. This will expand ''​COUNT''​ number of positional parameters ​starting ​at ''​START''​. ''​COUNT''​ can be omitted (''​${@:​START}''​),​ in this case all positional parameters beginning at ''​START''​ are expanded.+The rules for using ''​@''​ or ''​*''​ and quoting are the same as 
 +above. This will expand ''​COUNT''​ number of positional parameters 
 +beginning ​at ''​START''​. ''​COUNT''​ can be omitted (''​${@:​START}''​),​ in 
 +which caseall positional parameters beginning at ''​START''​ are 
 +expanded.
  
-If ''​START''​ is negative, the positional parameters are numbered ​from the last one backwards.+If ''​START''​ is negative, the positional parameters are numbered ​in reverse 
 +starting with the last one.
  
-''​COUNT''​ may not be negative, ​so elements are always counted in the forward direction.+''​COUNT''​ may not be negative, ​i.e. the element count may not be decremented.
  
 __**Example:​**__ __**Example:​**__
Line 186: Line 236:
 </​code>​ </​code>​
  
-:V4: __**Attention**__: ​Since Bash 4, a ''​START''​ of ''​0''​ includes the special parameter ''​$0'',​ i.e. the shell name or whatever ​it'​s ​set to, when the positional parameters are in use. A ''​START''​ of ''​1''​ begins at ''​$1''​. In Bash 3 and older, both ''​0''​ and ''​1''​ began at ''​$1''​.+__**Attention**__: ​As of Bash 4, a ''​START''​ of ''​0''​ includes the special parameter ''​$0'',​ i.e. the shell name or whatever ​$0 is set to, when the positional parameters are in use. A ''​START''​ of ''​1''​ begins at ''​$1''​. In Bash 3 and older, both ''​0''​ and ''​1''​ began at ''​$1''​.
  
 ===== Setting Positional Parameters ===== ===== Setting Positional Parameters =====
  
-Letting the caller set the positional parameters, ​by giving parameters on commandline, ​is not the only way to set them. +Setting ​positional parameters ​with command line arguments, 
-The [[ commands:​builtin:​set | set builtin command ]] can be used to "​artificially"​ change the positional parameters from inside the script or function:+is not the only way to set them. 
 +The [[ commands:​builtin:​set | builtin command, set ]] 
 +may be used to "​artificially"​ change the positional parameters from 
 +inside the script or function:
  
 <​code>​ <​code>​
Line 205: Line 258:
 </​code>​ </​code>​
  
-It's wise to signal "end of options"​ when setting positional parameters this way. If not, dashes might be interpreted as option ​tag by ''​set''​ itself:+It's wise to signal "end of options"​ when setting positional 
 +parameters this way. If not, the dashes might be interpreted as an option ​switch 
 +by ''​set''​ itself: 
 <​code>​ <​code>​
 # both ways work, but behave differently. See the article about the set command! # both ways work, but behave differently. See the article about the set command!
 set -- ... set -- ...
 set - ... set - ...
 +</​code>​
 +
 +Alternately this will also preserve any verbose (-v) or tracing (-x) flags, which may otherwise be reset by ''​set''​
 +<​code>​
 +set -$- ...
 </​code>​ </​code>​
  
Line 217: Line 278:
  
 ==== Using a while loop ==== ==== Using a while loop ====
-This one uses a [[syntax:​ccmd:​while_loop | while loop]] to switch from argument to argument, and a [[syntax:​ccmd:​case | case statement]] to interpret these arguments. 
-<​code>​ 
-while [ "​${1+isset}"​ ]; do 
-  case "​$1"​ in 
-    -x|--extmode) 
-      MY_EXTENDEDMODE=1 
-      shift 
-      ;; 
-    -u|--user) 
-      MY_USERNAME="​$2"​ 
-      shift 2 
-      ;; 
-    -f|--configfile) 
-      MY_CONFIG="​$2"​ 
-      shift 2 
-      ;; 
-    -h|--help) 
-      display_help # a function ;-) 
-      # no shifting needed here, we'll quit! 
-      exit 
-      ;; 
-    *) 
-      echo "​Error:​ Unknown option: $1" >&2 
-      exit 1 
-      ;; 
-  esac 
-done 
-</​code>​ 
  
-It`s not that flexible, it doesn'​t auto-interpret mixed options, and some other things, but it works and is a good rudimentary way to parse your arguments.+To make your program accept options as standard command syntax:
  
 +''​COMMAND [options] <​params>'' ​ # Like 'cat -A file.txt'​
  
-==== Tuned while loop ====+See simple option parsing code below. It's not that flexible. It 
 +doesn'​t auto-interpret combined options (-fu USER) but it works and is 
 +a good rudimentary way to parse your arguments.
  
-Basically ​it's the very same as the example above, but this one only reacts on arguments matching ''​-*''​ (beginning with a dash), otherwise it quitsThat way you can provide the parsing of the recommended UNIX(r)-argument-way:​+<​code>​ 
 +#!/bin/sh 
 +# Keeping options in alphabetical order makes it easy to add more.
  
-''​COMMAND <​options>​ <​mass-options>''​ +while : 
- +do 
-like +    case "​$1"​ in 
- +      -| --file
-''​cat -A *''​ +   file="​$2" ​  # You may want to check validity of $2 
- +   ​shift 2 
-where ''​*''​ might expand to thousands of filenames. +   ​;; 
- +      -| --help
-<​code>​ +   display_help ​ # Call your function 
-while [[ $1 = -* ]]; do +   # no shifting needed here, we're done. 
-  case "​$1"​ in +   exit 0 
-    -x|--extmode+   ​;; 
-      ​MY_EXTENDEDMODE=1 +      -| --user
-      shift +   username="​$2" ​# You may want to check validity of $2 
-      ;; +   ​shift 2 
-    -u|--user+   ​;; 
-      ​MY_USERNAME="​$2"​ +      -| --verbose
-      shift 2 +           ​It'​s better to assign ​string, than a number like "​verbose=1"​ 
-      ;; +   ​ ​because if you're debugging the script with "bash -x" code like this: 
-    -f|--configfile+   # 
-      ​MY_CONFIG="​$2"​ +   #    if [ "​$verbose"​ ] ... 
-      shift 2 +   # 
-      ;; +   #  You will see: 
-    -h|--help+   # 
-      ​display_help ​# a function ;-) +   #    if [ "​verbose"​ ] ... 
-      no shifting needed here, we'll quit! +   # 
-      exit +          #  Instead of cryptic 
-      ;; +   # 
-    *+   #    if [ "​1"​ ] ... 
-      echo "​Error:​ Unknown option: $1" >&​2 +   # 
-      exit 1 +   verbose="​verbose"​ 
-      ;; +   shift 
-  esac+   ​;; 
 +      --# End of all options 
 +   shift 
 +   break; 
 +      ​-*) 
 +   ​echo "​Error:​ Unknown option: $1" >&​2 
 +   ​exit 1 
 +   ​;; 
 +      ​*) ​ # No more options 
 +   break 
 +   ;; 
 +    ​esac
 done done
  
-echo "​Processing files..."​ +End of file
- +
-for file; do +
-  echo "File: $arg"​ +
-  ​... +
-done+
 </​code>​ </​code>​
  
-Due to the shifting in the while-loop, the mass-arguments begin at ''​$1'',​ they'​re fully accessible by ''"​$@"''​ or ''​for i''​.+==== Filter unwanted options with a wrapper script ====
  
-Note the ''​while''​-condition: It's not the test command, ​it'the [[syntax:​ccmd:​conditional_expression | conditional expression]],​ since we do [[syntax:​pattern | pattern matching]].+This simple wrapper enables filtering unwanted options (here: ​''​-a''​ 
 +and ''​--all''​ for ''​ls'​') out of the command ​line. It reads the 
 +positional parameters and builds a filtered array consisting of themthen 
 +calls ''​ls''​ with the new option setIt also respects the ''​--''​ 
 +as "end of options"​ for ''​ls''​ and doesn'​t change anything after it:
  
- 
- 
- 
-==== Filter unwanted options with a wrapper script ==== 
-This simple wrapper allows to filter unwanted options (here: ''​-a''​ and ''​--all''​ for ''​ls''​) out of the commandline. It reads the positional parameters and builds a (filtered) array out of them, then it calls ''​ls''​ with the new option set. It also respects the ''​--''​ as "end of options"​ for ''​ls''​ and doesn'​t change anything after it: 
 <​code>​ <​code>​
 #!/bin/bash #!/bin/bash
Line 310: Line 353:
 # simple ls(1) wrapper that doesn'​t allow the -a option # simple ls(1) wrapper that doesn'​t allow the -a option
  
-EOO=0 # end of options reached+options=() ​ # the buffer array for the parameters 
 +eoo=0       ​# end of options reached
  
-while [[ $1 ]]do +while [[ $1 ]] 
-if ! ((EOO)); then +do 
-  case "​$1"​ in +    if ! ((eoo)); then 
-    -[^-]*a*|-a?​*) + case "​$1"​ in 
-      ​OPTIONS+=("​${1//​a}"​) +   -a) 
-      shift +       shift 
-      ;; +       ;; 
-    -a) +   --all) 
-      shift +       shift 
-      ;; +       ;; 
-    --all) +   ​-[^-]*a*|-a?​*) 
-      shift +       options+=("​${1//​a}"​) 
-      ;; +       ​shift 
-    ​--) +       ​;; 
-      EOO=1 +   ​--) 
-      ​OPTIONS+=("​$1"​) +       eoo=1 
-      shift +       options+=("​$1"​) 
-      ;; +       ​shift 
-    *) +       ​;; 
-      ​OPTIONS+=("​$1"​) +   ​*) 
-      shift +       options+=("​$1"​) 
-      ;; +       ​shift 
-  esac +       ​;; 
-else + esac 
-  ​OPTIONS+=("​$1"​) +    else 
-  # Another (worse) way of doing the same thing: + options+=("​$1"​) 
-  OPTIONS=("${OPTIONS[@]}" "​$1"​) + 
-  shift + # Another (worse) way of doing the same thing: 
-fi+options=("${options[@]}" "​$1"​) 
 + shift 
 +    fi
 done done
  
-/bin/ls "${OPTIONS[@]}"+/bin/ls "${options[@]}"
 </​code>​ </​code>​
- 
- 
  
 ==== Using getopts ==== ==== Using getopts ====
  
 There is a [[howto:​getopts_tutorial|small tutorial dedicated to ''​getopts''​]] (//under construction//​). There is a [[howto:​getopts_tutorial|small tutorial dedicated to ''​getopts''​]] (//under construction//​).
- 
- 
  
 ===== See also ===== ===== See also =====
  • scripting/posparams.txt
  • Last modified: 2018/05/12 18:04
  • by wayeoyuz