bash-hackers-wiki/syntax/ccmd/conditional_expression/index.html

74 lines
55 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/syntax/ccmd/conditional_expression/ rel=canonical><link href=../classic_for/ rel=prev><link href=../grouping_plain/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.5.44"><title>The conditional expression - 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=#the-conditional-expression 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> The conditional expression </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)
</code></pre></div> <h2 id=description>Description<a class=headerlink href=#description title="Permanent link">&para;</a></h2> <p>The conditional expression is meant as the modern variant of the <a href=../../../commands/classictest/ >classic test command</a>. Since it is <strong>not</strong> a normal command, Bash doesn't need to apply the normal commandline parsing rules like recognizing <code>&amp;&amp;</code> as <a href=../../basicgrammar/#lists>command list</a> operator.</p> <p>The testing features basically are the same (see the lists for <a href=../../../commands/classictest/ >classic test command</a>), with some additions and extensions.</p> <table> <thead> <tr> <th>Operator</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>( &lt;EXPRESSION&gt; )</code></td> <td>Used to group expressions, to influence precedence of operators</td> </tr> <tr> <td><code>&lt;EXPRESSION1&gt; &amp;&amp; &lt;EXPRESSION2&gt;</code></td> <td><code>TRUE</code> if <code>&lt;EXPRESSION1&gt;</code><strong>and</strong><code>&lt;EXPRESSION2&gt;</code> are <code>TRUE</code> (do <strong>not</strong> use <code>-a</code>!)</td> </tr> <tr> <td><code>&lt;EXPRESSION1&gt;||&lt;EXPRESSION2&gt;</code></td> <td><code>TRUE</code> if <code>&lt;EXPRESSION1&gt;</code><strong>or</strong><code>&lt;EXPRESSION2&gt;</code> is <code>TRUE</code> (do <strong>not</strong> use <code>-o</code>!)</td> </tr> <tr> <td><code>&lt;STRING&gt; == &lt;PATTERN&gt;</code></td> <td><code>&lt;STRING&gt;</code> is checked against the pattern <code>&lt;PATTERN&gt;</code> - <code>TRUE</code> on a match<br><em>But note¹, quoting the pattern forces a literal comparison.</em></td> </tr> <tr> <td><code>&lt;STRING&gt; = &lt;PATTERN&gt;</code></td> <td>equivalent to the <code>==</code> operator</td> </tr> <tr> <td><code>&lt;STRING&gt; != &lt;PATTERN&gt;</code></td> <td><code>&lt;STRING&gt;</code> is checked against the pattern <code>&lt;PATTERN&gt;</code> - <code>TRUE</code> on <strong>no match</strong></td> </tr> <tr> <td><code>&lt;STRING&gt; =~ &lt;ERE&gt;</code></td> <td><code>&lt;STRING&gt;</code> is checked against the <a href=https://en.wikipedia.org/wiki/Regular_expression#POSIX_extended>extended regular expression</a> <code>&lt;ERE&gt;</code> - <code>TRUE</code> on a match</td> </tr> <tr> <td>See the <a href=../../../commands/classictest/#file_tests>classic test operators</a></td> <td>Do <strong>not</strong> use the <code>test</code>-typical operators <code>-a</code> and <code>-o</code> for AND and OR.</td> </tr> <tr> <td>See also <a href=../../arith_expr/#comparisons>arithmetic comparisons</a></td> <td>Using <code>(( &lt;EXPRESSION&gt; ))</code>, the <a href=../arithmetic_eval/ >arithmetic expression compound command</a></td> </tr> </tbody> </table> <p>When the <code>==</code> and <code>!=</code> operators are used, the string to the right of the operator is considered a pattern and matched according to the rules of <a href=../../pattern/ >Pattern Matching</a>. If the shell option <code>nocasematch</code> is enabled, the match is performed without regard to the case of alphabetic characters.</p> <p>¹Any part of the pattern may be quoted to force it to be matched as a literal string.</p> <p>When the operators <code>&lt;</code> and <code>&gt;</code> are used (string collation order), the test happens using the current locale when the <code>compat</code> level is greater than "40".</p> <p>Operator precedence (highest =&gt; lowest):</p> <ul> <li><code>( &lt;EXPRESSION&gt; )</code></li> <li><code>! &lt;EXPRESSION&gt;</code></li> <li><code>&lt;EXPRESSION1&gt; &amp;&amp; &lt;EXPRESSION2&gt;</code></li> <li><code>&lt;EXPRESSION1&gt; || &lt;EXPRESSION2&gt;</code></li> </ul> <p>Do <strong>not</strong> use the <code>test</code>-typical operators <code>-a</code> and <code>-o</code> for AND and OR, they are not known to the conditional expression. Instead, use the operators <code>&amp;&amp;</code> and <code>||</code>.</p> <h3 id=word-splitting>Word splitting<a class=headerlink href=#word-splitting title="Permanent link">&para;</a></h3> <p><a href=../../expansion/wordsplit/ >Word
checkme=&quot;Be liberal in what you accept, and conservative in what you send&quot;
if [[ $sentence == $checkme ]]; then
echo &quot;Matched...!&quot;
else
echo &quot;Sorry, no match :-(&quot;
fi
</code></pre></div> <p>Compare that to the <a href=../../../commands/classictest/ >classic test command</a>, where word splitting is done (because it's a normal command, not something special):</p> <div class=highlight><pre><span></span><code>sentence=&quot;Be liberal in what you accept, and conservative in what you send&quot;
checkme=&quot;Be liberal in what you accept, and conservative in what you send&quot;
if [ &quot;$sentence&quot; == &quot;$checkme&quot; ]; then
echo &quot;Matched...!&quot;
else
echo &quot;Sorry, no match :-(&quot;
fi
</code></pre></div> <p>You need to quote that variable reference in the classic test command, since (due to the spaces) the word splitting will break it otherwise!</p> <h3 id=regular-expression-matching>Regular Expression Matching<a class=headerlink href=#regular-expression-matching title="Permanent link">&para;</a></h3> <p>Using the operator <code>=~</code>, the left hand side operand is matched against the <strong>extended regular expression (ERE)</strong> on the right hand side.</p> <p>This is consistent with matching against patterns: Every quoted part of the regular expression is taken literally, even if it contains regular expression special characters.</p> <p>Best practise is to put the regular expression to match against into a variable. This is to avoid shell parsing errors on otherwise valid regular expressions.</p> <div class=highlight><pre><span></span><code>REGEX=&quot;^[[:upper:]]{2}[[:lower:]]*$&quot;
# Test 1
STRING=Hello
if [[ $STRING =~ $REGEX ]]; then
echo &quot;Match.&quot;
else
echo &quot;No match.&quot;
fi
# ==&gt; &quot;No match.&quot;
# Test 2
STRING=HEllo
if [[ $STRING =~ $REGEX ]]; then
echo &quot;Match.&quot;
else
echo &quot;No match.&quot;
fi
# ==&gt; &quot;Match.&quot;
</code></pre></div> <p>The interpretation of quoted regular expression special characters can be influenced by setting the <code>compat31</code> and <code>compat32</code> shell options (<code>compat*</code> in general). See <a href=../../../internals/shell_options/ >shell_options</a>.</p> <h4 id=the-special-bash_rematch-array-variable>The special BASH_REMATCH array variable<a class=headerlink href=#the-special-bash_rematch-array-variable title="Permanent link">&para;</a></h4> <p>An array variable whose members are assigned by the <code>=~</code> binary operator to the <code>[[</code> conditional command.</p> <p>The element with index 0 is the portion of the string matching the entire regular expression. The element with index n is the portion of the string matching the nth parenthesized subexpression.</p> <p>See <a href=../../shellvars/#bash_rematch>BASH_REMATCH</a>.</p> <p>Example:</p> <div class=highlight><pre><span></span><code>if [[ &quot;The quick, red fox&quot; =~ ^The\ (.*),\ (.*)\ fox$ ]]; then
echo &quot;${BASH_REMATCH[0]} is ${BASH_REMATCH[1]} and ${BASH_REMATCH[2]}.&quot;;
fi
==&gt; The quick, red fox is quick and red.
</code></pre></div> <h3 id=behaviour-differences-compared-to-the-builtin-test-command>Behaviour differences compared to the builtin test command<a class=headerlink href=#behaviour-differences-compared-to-the-builtin-test-command title="Permanent link">&para;</a></h3> <p>As of Bash 4.1 alpha, the test primaries '&lt;' and '&gt;' (compare strings lexicographically) use the current locale settings, while the same primitives for the builtin test command don't. This leads to the following situation where they behave differently:</p> <div class=highlight><pre><span></span><code>$ ./cond.sh
[[ &#39; 4&#39; &lt; &#39;1&#39; ]] --&gt; exit 1
[[ &#39;step+&#39; &lt; &#39;step-&#39; ]] --&gt; exit 1
[ &#39; 4&#39; &lt; &#39;1&#39; ] --&gt; exit 0
[ &#39;step+&#39; &lt; &#39;step-&#39; ] --&gt; exit 0
</code></pre></div> <p>It won't be aligned. The conditional expression continues to respect the locate, as introduced with 4.1-alpha, the builtin <code>test</code>/<code>[</code> command continues to behave differently.</p> <h3 id=implicit-arithmetic-context>Implicit arithmetic context<a class=headerlink href=#implicit-arithmetic-context title="Permanent link">&para;</a></h3> <p>When you use a numeric comparison, the arguments are evaluated as an arithmetic expression. The arithmetic expression must be quoted if it both contains whitespace and is not the result of an expansion.</p> <div class=highlight><pre><span></span><code>[[ &#39;i=5, i+=2&#39; -eq 3+4 ]] &amp;&amp; echo true # prints true.
</code></pre></div> <h2 id=examples>Examples<a class=headerlink href=#examples title="Permanent link">&para;</a></h2> <h2 id=portability-considerations>Portability considerations<a class=headerlink href=#portability-considerations title="Permanent link">&para;</a></h2> <ul> <li><code>[[ ... ]]</code> functionality isn't specified by POSIX(R), though it's a reserved word</li> <li>Amongst the major "POSIX-shell superset languages" (for lack of a better term) which do have <code>[[</code>, the test expression compound command is one of the very most portable non-POSIX features. Aside from the <code>=~</code> operator, almost every major feature is consistent between Ksh88, Ksh93, mksh, Zsh, and Bash. Ksh93 also adds a large number of unique pattern matching features not supported by other shells including support for several different regex dialects, which are invoked using a different syntax from Bash's <code>=~</code>, though <code>=~</code> is still supported by ksh and defaults to ERE.</li> <li>As an extension to POSIX ERE, most GNU software supports backreferences in ERE, including Bash. According to POSIX, only BRE is supposed to support them. This requires Bash to be linked against glibc, so it won't necessarily work on all platforms. For example, <code>$(m='(abc(def))(\1)(\2)'; [[ abcdefabcdefdef =~ $m ]]; printf '&lt;%s&gt; ' $? "${BASH_REMATCH[@]}" )</code> will give <code>&lt;0&gt; &lt;abcdefabcdefdef&gt; &lt;abcdef&gt; &lt;def&gt; &lt;abcdef&gt; &lt;def&gt;</code>.</li> <li>the <code>=~</code> (regex) operator was introduced in Bash 3.0, and its behaviour changed in Bash 3.2: since 3.2, quoted strings and substrings are matched as literals by default.</li> <li>the behaviour of the <code>&lt;</code> and <code>&gt;</code> operators (string collation order) has changed since Bash 4.0</li> </ul> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">&para;</a></h2> <ul> <li>Internal: <a href=../../pattern/ >pattern matching language</a></li> <li>Internal: <a href=../../../commands/classictest/ >the classic test command</a></li> <li>Internal: <a href=../if_clause/ >the if-clause</a></li> <li><a href=http://mywiki.wooledge.org/BashFAQ/031>What is the difference between test, [ and [[ ?</a> - BashFAQ 31 - Greg's wiki.</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>