bash-hackers-wiki/scripting/posparams/index.html

169 lines
61 KiB
HTML
Raw Permalink Normal View History

<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link href=https://flokoe.github.io/bash-hackers-wiki/scripting/posparams/ rel=canonical><link href=../obsolete/ rel=prev><link href=../processtree/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.5.44"><title>Handling positional parameters - The Bash Hackers Wiki</title><link rel=stylesheet href=../../assets/stylesheets/main.0253249f.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.06af60db.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#handling-positional-parameters class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../.. title="The Bash Hackers Wiki" class="md-header__button md-logo" aria-label="The Bash Hackers Wiki" data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> The Bash Hackers Wiki </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Handling positional parameters </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=indigo data-md-color-accent=indigo aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media)
echo &quot;$0&quot;
</code></pre></div> <p>You see, <code>$0</code> is always set to the name the script is called with (<code>&gt;</code> is the prompt...):</p> <div class=highlight><pre><span></span><code>&gt; ./testscript
./testscript
&gt; /usr/bin/testscript
/usr/bin/testscript
</code></pre></div> <p>However, this isn't true for login shells:</p> <div class=highlight><pre><span></span><code>&gt; echo &quot;$0&quot;
-bash
</code></pre></div> <p>In other terms, <code>$0</code> is not a positional parameter, it's a special parameter independent from the positional parameter list. It can be set to anything. In the <strong>ideal</strong> case it's the pathname of the script, but since this gets set on invocation, the invoking program can easily influence it (the <code>login</code> program does that for login shells, by prefixing a dash, for example).</p> <p>Inside a function, <code>$0</code> still behaves as described above. To get the function name, use <code>$FUNCNAME</code>.</p> <h2 id=shifting>Shifting<a class=headerlink href=#shifting title="Permanent link">&para;</a></h2> <p>The builtin command <code>shift</code> is used to change the positional parameter values:</p> <ul> <li><code>$1</code> will be discarded</li> <li><code>$2</code> will become <code>$1</code></li> <li><code>$3</code> will become <code>$2</code></li> <li>...</li> <li>in general: <code>$N</code> will become <code>$N-1</code></li> </ul> <p>The command can take a number as argument: Number of positions to shift. e.g. <code>shift 4</code> shifts <code>$5</code> to <code>$1</code>.</p> <h2 id=using-them>Using them<a class=headerlink href=#using-them title="Permanent link">&para;</a></h2> <p>Enough theory, you want to access your script-arguments. Well, here we go.</p> <h3 id=one-by-one>One by one<a class=headerlink href=#one-by-one title="Permanent link">&para;</a></h3> <p>One way is to access specific parameters:</p> <div class=highlight><pre><span></span><code>#!/bin/bash
echo &quot;Total number of arguments: $#&quot;
echo &quot;Argument 1: $1&quot;
echo &quot;Argument 2: $2&quot;
echo &quot;Argument 3: $3&quot;
echo &quot;Argument 4: $4&quot;
echo &quot;Argument 5: $5&quot;
</code></pre></div> <p>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.</p> <p>=&gt; forget that one</p> <h3 id=loops>Loops<a class=headerlink href=#loops title="Permanent link">&para;</a></h3> <p>There are several ways to loop through the positional parameters.</p> <hr> <p>You can code a <a href=../../syntax/ccmd/c_for/ >C-style for-loop</a> using <code>$#</code> as the end value. On every iteration, the <code>shift</code>-command is used to shift the argument list:</p> <div class=highlight><pre><span></span><code>numargs=$#
for ((i=1 ; i &lt;= numargs ; i++))
do
echo &quot;$1&quot;
shift
done
</code></pre></div> <p>Not very stylish, but usable. The <code>numargs</code> variable is used to store the initial value of <code>$#</code> because the shift command will change it as the script runs.</p> <hr> <p>Another way to iterate one argument at a time is the <code>for</code> loop without a given wordlist. The loop uses the positional parameters as a wordlist:</p> <div class=highlight><pre><span></span><code>for arg
do
echo &quot;$arg&quot;
done
</code></pre></div> <p><u>Advantage:</u> The positional parameters will be preserved</p> <hr> <p>The next method is similar to the first example (the <code>for</code> loop), but it doesn't test for reaching <code>$#</code>. It shifts and checks if <code>$1</code> still expands to something, using the <a href=../../commands/classictest/ >test command</a>:</p> <div class=highlight><pre><span></span><code>while [ &quot;$1&quot; ]
do
echo &quot;$1&quot;
shift
done
</code></pre></div> <p>Looks nice, but has the disadvantage of stopping when <code>$1</code> is empty (null-string). Let's modify it to run as long as <code>$1</code> is defined (but may be null), using <a href=../../syntax/pe/#use_an_alternate_value>parameter expansion for an alternate value</a>:</p> <div class=highlight><pre><span></span><code>while [ &quot;${1+defined}&quot; ]; do
echo &quot;$1&quot;
shift
done
</code></pre></div> <h3 id=getopts>Getopts<a class=headerlink href=#getopts title="Permanent link">&para;</a></h3> <p>There is a <a href=../../howto/getopts_tutorial/ >small tutorial dedicated to <code>getopts</code></a> (<em>under construction</em>).</p> <h2 id=mass-usage>Mass usage<a class=headerlink href=#mass-usage title="Permanent link">&para;</a></h2> <h3 id=all-positional-parameters>All Positional Parameters<a class=headerlink href=#all-positional-parameters title="Permanent link">&para;</a></h3> <p>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!).</p> <p>The shell developers created <code>$*</code> and <code>$@</code> for this purpose.</p> <p>As overview:</p> <table> <thead> <tr> <th>Syntax</th> <th>Effective result</th> </tr> </thead> <tbody> <tr> <td><code>$*</code></td> <td><code>$1 $2 $3 ... ${N}</code></td> </tr> <tr> <td><code>$@</code></td> <td><code>$1 $2 $3 ... ${N}</code></td> </tr> <tr> <td><code>"$*"</code></td> <td><code>"$1c$2c$3c...c${N}"</code></td> </tr> <tr> <td><code>"$@"</code></td> <td><code>"$1" "$2" "$3" ... "${N}"</code></td> </tr> </tbody> </table> <p>Without being quoted (double quotes), both have the same effect: All positional parameters from <code>$1</code> to the last one used are expanded without any special handling.</p> <p>When the <code>$*</code> special parameter is double quoted, it expands to the equivalent of: <code>"$1c$2c$3c$4c........$N"</code>, where 'c' is the first character of <code>IFS</code>.</p> <p>But when the <code>$@</code> special parameter is used inside double quotes, it expands to the equivanent of...</p> <p><code>"$1" "$2" "$3" "$4" ..... "$N"</code></p> <p>...which <strong>reflects all positional parameters as they were set initially</strong> and passed to the script or function. If you want to re-use your positional parameters to <strong>call another program</strong> (for example in a wrapper-script), then this is the choice for you, use double quoted <code>"$@"</code>.</p> <p>Well, let's just say: <strong>You almost always want a quoted <code>"$@"</code>!</strong></p> <h3 id=range-of-positional-parameters>Range Of Positional Parameters<a class=headerlink href=#range-of-positional-parameters title="Permanent link">&para;</a></h3> <p>Another way to mass expand the positional parameters is similar to what is possible for a range of characters using <a href=../../syntax/pe/#substring_expansion>substring expansion</a> on normal parameters and the mass expansion range of <a href=../../syntax/arrays/ >arrays</a>.</p> <p><code>${@:START:COUNT}</code></p> <p><code>${*:START:COUNT}</code></p> <p><code>"${@:START:COUNT}"</code></p> <p><code>"${*:START:COUNT}"</code></p> <p>The rules for using <code>@</code> or <code>*</code> and quoting are the same as above. This will expand <code>COUNT</code> number of positional parameters beginning at <code>START</code>. <code>COUNT</code> can be omitted (<code>${@:START}</code>), in which case, all positional parameters beginning at <code>START</code> are expanded.</p> <p>If <code>START</code> is negative, the positional parameters are numbered in reverse starting with the last one.</p> <p><code>COUNT</code> may not be negative, i.e. the element count may not be decremented.</p> <p><u><strong>Example:</strong></u> START at the last positional parameter:</p> <div class=highlight><pre><span></span><code>echo &quot;${@: -1}&quot;
</code></pre></div> <p><u><strong>Attention</strong></u>: As of Bash 4, a <code>START</code> of <code>0</code> includes the special parameter <code>$0</code>, i.e. the shell name or whatever <code>$0</code> is set to, when the positional parameters are in use. A <code>START</code> of <code>1</code> begins at <code>$1</code>. In Bash 3 and older, both <code>0</code> and <code>1</code> began at <code>$1</code>.</p> <h2 id=setting-positional-parameters>Setting Positional Parameters<a class=headerlink href=#setting-positional-parameters title="Permanent link">&para;</a></h2> <p>Setting positional parameters with command line arguments, is not the only way to set them. The <a href=../../commands/builtin/set/ >builtin command, set</a> may be used to "artificially" change the positional parameters from inside the script or function:</p> <div class=highlight><pre><span></span><code>set &quot;This is&quot; my new &quot;set of&quot; positional parameters
# RESULTS IN
# $1: This is
# $2: my
# $3: new
# $4: set of
# $5: positional
# $6: parameters
</code></pre></div> <p>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 <code>set</code> itself:</p> <div class=highlight><pre><span></span><code># both ways work, but behave differently. See the article about the set command!
set -- ...
set - ...
</code></pre></div> <p>Alternately this will also preserve any verbose (-v) or tracing (-x) flags, which may otherwise be reset by <code>set</code></p> <div class=highlight><pre><span></span><code>set -$- ...
</code></pre></div> <div class="admonition warning"> <p class=admonition-title>FIXME</p> <p>continue</p> </div> <h2 id=production-examples>Production examples<a class=headerlink href=#production-examples title="Permanent link">&para;</a></h2> <h3 id=using-a-while-loop>Using a while loop<a class=headerlink href=#using-a-while-loop title="Permanent link">&para;</a></h3> <p>To make your program accept options as standard command syntax:</p> <p><code>COMMAND [options] &lt;params&gt; # Like 'cat -A file.txt'</code></p> <p>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.</p> <div class=highlight><pre><span></span><code>#!/bin/sh
# Keeping options in alphabetical order makes it easy to add more.
while :
do
case &quot;$1&quot; in
-f | --file)
file=&quot;$2&quot; # You may want to check validity of $2
shift 2
;;
-h | --help)
display_help # Call your function
# no shifting needed here, we&#39;re done.
exit 0
;;
-u | --user)
username=&quot;$2&quot; # You may want to check validity of $2
shift 2
;;
-v | --verbose)
# It&#39;s better to assign a string, than a number like &quot;verbose=1&quot;
# because if you&#39;re debugging the script with &quot;bash -x&quot; code like this:
#
# if [ &quot;$verbose&quot; ] ...
#
# You will see:
#
# if [ &quot;verbose&quot; ] ...
#
# Instead of cryptic
#
# if [ &quot;1&quot; ] ...
#
verbose=&quot;verbose&quot;
shift
;;
--) # End of all options
shift
break;
-*)
echo &quot;Error: Unknown option: $1&quot; &gt;&amp;2
exit 1
;;
*) # No more options
break
;;
esac
done
# End of file
</code></pre></div> <h3 id=filter-unwanted-options-with-a-wrapper-script>Filter unwanted options with a wrapper script<a class=headerlink href=#filter-unwanted-options-with-a-wrapper-script title="Permanent link">&para;</a></h3> <p>This simple wrapper enables filtering unwanted options (here: <code>-a</code> and <code>--all</code> for <code>ls</code>) out of the command line. It reads the positional parameters and builds a filtered array consisting of them, then calls <code>ls</code> with the new option set. It also respects the <code>--</code> as "end of options" for <code>ls</code> and doesn't change anything after it:</p> <div class=highlight><pre><span></span><code>#!/bin/bash
# simple ls(1) wrapper that doesn&#39;t allow the -a option
options=() # the buffer array for the parameters
eoo=0 # end of options reached
while [[ $1 ]]
do
if ! ((eoo)); then
case &quot;$1&quot; in
-a)
shift
;;
--all)
shift
;;
-[^-]*a*|-a?*)
options+=(&quot;${1//a}&quot;)
shift
;;
--)
eoo=1
options+=(&quot;$1&quot;)
shift
;;
*)
options+=(&quot;$1&quot;)
shift
;;
esac
else
options+=(&quot;$1&quot;)
# Another (worse) way of doing the same thing:
# options=(&quot;${options[@]}&quot; &quot;$1&quot;)
shift
fi
done
/bin/ls &quot;${options[@]}&quot;
</code></pre></div> <h3 id=using-getopts>Using getopts<a class=headerlink href=#using-getopts title="Permanent link">&para;</a></h3> <p>There is a <a href=../../howto/getopts_tutorial/ >small tutorial dedicated to <code>getopts</code></a> (<em>under construction</em>).</p> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">&para;</a></h2> <ul> <li>Internal: <a href=../../howto/getopts_tutorial/ >getopts_tutorial</a></li> <li>Internal: <a href=../../syntax/ccmd/while_loop/ >while_loop</a></li> <li>Internal: <a href=../../syntax/ccmd/c_for/ >c_for</a></li> <li>Internal: <a href=../../syntax/arrays/ >arrays</a> (for equivalent syntax for mass-expansion)</li> <li>Internal: <a href=../../syntax/pe/#substring_expansion>Substring expansion on a parameter</a> (for equivalent syntax for mass-expansion)</li> <li>Dictionary, internal: <a href=../../dict/parameter/ >parameter</a></li> </ul> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">November 13, 2024</span> </span> <span class=md-source-file__fact> <span class=md-icon title=Created> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M14.47 15.08 11 13V7h1.5v5.25l3.08 1.83c-.41.28-.79.62-1.11 1m-1.39 4.84c-.36.05-.71.08-1.08.08-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8c0 .37-.03.72-.08 1.08.69.1 1.33.32 1.92.64.1-.56.16-1.13.16-1.72 0-5.5-4.5-10-10-10S2 6.5 2 12s4.47 10 10 10c.59 0 1.16-.06 1.72-.16-.32-.59-.54-1.23-.64-1.92M18 15v3h-3v2h3v3h2v-3h3v-2h-3v-3z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">November 13, 2024</span> </span> </aside> <h2 id=__comments>Comments</h2> <script src=https://giscus.app/client.js data-repo=flokoe/bash-hackers-wiki data-repo-id=R_kgDOJ3Nr6Q data-category="Giscus Page Comments" data-category-id=DIC_kwDOJ3Nr6c4CXq9t data-mapping=pathname data-strict=1 data-reactions-enabled=1 data-emit-metadata=0 data-input-position=top data-theme=preferred_color_scheme data-lang=en data-loading=lazy crossorigin=anonymous async>
</script> <script>
var giscus = document.querySelector("script[src*=giscus]")
/* Set palette on initial load */
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate" ? "dark" : "light"
giscus.setAttribute("data-theme", theme)
}
/* Register event handlers after documented loaded */
document.addEventListener("DOMContentLoaded", function() {
var ref = document.querySelector("[data-md-component=palette]")
ref.addEventListener("change", function() {
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate" ? "dark" : "light"
/* Instruct Giscus to change theme */
var frame = document.querySelector(".giscus-frame")
frame.contentWindow.postMessage(
{ giscus: { setConfig: { theme } } },
"https://giscus.app"
)
}
})
})
</script> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"base": "../..", "features": ["navigation.instant", "navigation.tracking", "navigation.tabs", "navigation.sections", "navigation.top", "content.action.view", "content.action.edit", "search.suggest", "search.highlight", "content.code.copy"], "search": "../../assets/javascripts/workers/search.6ce7567c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script> <script src=../../assets/javascripts/bundle.83f73b43.min.js></script> </body> </html>