mirror of
https://github.com/flokoe/bash-hackers-wiki.git
synced 2024-11-25 15:53:41 +01:00
237 lines
103 KiB
HTML
237 lines
103 KiB
HTML
|
<!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/commands/classictest/ rel=canonical><link rel=prev href=../..><link href=../builtin/caller/ 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 classic test command - 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-classic-test-command 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 classic test command </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){var media=matchMedia
|
|||
|
<a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>
|
|||
|
<a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=c1># test if /etc/passwd exists</span>
|
|||
|
<a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=k>if</span><span class=w> </span><span class=nb>test</span><span class=w> </span>-e<span class=w> </span>/etc/passwd<span class=p>;</span><span class=w> </span><span class=k>then</span>
|
|||
|
<a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"Alright man..."</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=k>else</span>
|
|||
|
<a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"Yuck! Where is it??"</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span><span class=nb>exit</span><span class=w> </span><span class=m>1</span>
|
|||
|
<a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>The syntax of the test command is relatively easy. Usually it's the command name <code>test</code> followed by a test type (here <code>-e</code> for "file exists") followed by test-type-specific values (here the filename to check, <code>/etc/passwd</code>).</p> <p>There's a second standardized command that does exactly the same: the command <code>[</code> – the difference just is that it's called <code>[</code> and the last argument to the command must be a <code>]</code>: It forms <code>[ <EXPRESSION> ]</code>.</p> <p>Let's rewrite the above example to use it:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=ch>#!/bin/bash</span>
|
|||
|
<a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>
|
|||
|
<a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=c1># test if /etc/passwd exists</span>
|
|||
|
<a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span>/etc/passwd<span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span>
|
|||
|
<a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"Alright man..."</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=k>else</span>
|
|||
|
<a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"Yuck! Where is it??"</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=nb>exit</span><span class=w> </span><span class=m>1</span>
|
|||
|
<a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>One might <strong>think</strong> now that these <code>[</code> and <code>]</code> belong to the syntax of Bash's if-clause: <strong>No they don't! It's a simple, ordinary command, still!</strong></p> <p>Another thing you have to remember is that if the test command wants one parameter for a test, you have to give it one parameter. Let's check for some of your music files:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=ch>#!/bin/bash</span>
|
|||
|
<a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>
|
|||
|
<a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=nv>mymusic</span><span class=o>=</span><span class=s2>"/data/music/Van Halen/Van Halen - Right Now.mp3"</span>
|
|||
|
<a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>
|
|||
|
<a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span><span class=s2>"</span><span class=nv>$mymusic</span><span class=s2>"</span><span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span>
|
|||
|
<a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"Let's rock"</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=k>else</span>
|
|||
|
<a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"No music today, sorry..."</span><span class=w> </span>><span class=p>&</span><span class=m>2</span>
|
|||
|
<a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=w> </span><span class=nb>exit</span><span class=w> </span><span class=m>1</span>
|
|||
|
<a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>As you definitely noted, the filename contains spaces. Since we call a normal ordinary command (<code>test</code> or <code>[</code>) the shell will word-split the expansion of the variable <code>mymusic</code>: You need to quote it when you don't want the <code>test</code>-command to complain about too many arguments for this test-type! If you didn't understand it, please read the <a href=../../syntax/words/ >article about words...</a>.</p> <p>Please also note that the file-tests want <strong>one filename</strong> to test. Don't give a glob (filename-wildcards) as it can expand to many filenames => <strong>too many arguments!</strong></p> <p><strong>Another common mistake</strong> is to provide too <strong>few</strong> arguments:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=o>[</span><span class=w> </span><span class=s2>"</span><span class=nv>$mystring</span><span class=s2>"</span>!<span class=o>=</span><span class=s2>"test"</span><span class=w> </span><span class=o>]</span>
|
|||
|
</code></pre></div> <p>This provides exactly <strong>one</strong> test-argument to the command. With one parameter, it defaults to the <code>-n</code> test: It tests if a provided string is empty (<code>FALSE</code>) or not (<code>TRUE</code>) - due to the lack of <strong>spaces to separate the arguments</strong> the shown command always ends <code>TRUE</code>!</p> <p>Well, I addressed several basic rules, now let's see what the test-command can do for you. The Bash test-types can be split into several sections: <strong>file tests</strong>, <strong>string tests</strong>, <strong>arithmetic tests</strong>, <strong>misc tests</strong>. Below, the tests marked with <img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/26a0.svg title=:warning:> are non-standard tests (i.e. not in SUS/POSIX/etc..).</p> <h2 id=file-tests>File tests<a class=headerlink href=#file-tests title="Permanent link">¶</a></h2> <p>This section probably holds the most tests, I'll list them in some logical order. Since Bash 4.1, all tests related to permissions respect ACLs, if the underlying filesystem/OS supports them.</p> <table> <thead> <tr> <th>Operator syntax</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><strong>-a</strong> <file></td> <td>True if <file> exists. <img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/26a0.svg title=:warning:> (not recommended, may collide with <code>-a</code> for <code>AND</code>, see below)</td> </tr> <tr> <td><strong>-e</strong> <file></td> <td>True if <file> exists.</td> </tr> <tr> <td><strong>-f</strong> <file></td> <td>True, if <file> exists and is a <strong>regular</strong> file.</td> </tr> <tr> <td><strong>-d</strong> <file></td> <td>True, if <file> exists and is a <strong>directory</strong>.</td> </tr> <tr> <td><strong>-c</strong> <file></td> <td>True, if <file> exists and is a <strong>character special</strong> file.</td> </tr> <tr> <td><strong>-b</strong> <file></td> <td>True, if <file> exists and is a <strong>block special</strong> file.</td> </tr> <tr> <td><strong>-p</strong> <file></td> <td>True, if <file> exists and is a <strong>named pipe</strong> (FIFO).</td> </tr> <tr> <td><strong>-S</strong> <file></td> <td>True, if <file> exists and is a <strong>socket</strong> file.</td> </tr> <tr> <td><strong>-L</strong> <file></td> <td>True, if <file> exists and is a <strong>symbolic link</strong>.</td> </tr> <tr> <td><strong>-h</strong> <file></td> <td>True, if <file> exists and is a <strong>symbolic link</strong>.</td> </tr> <tr> <td><strong>-g</strong> <file></td> <td>True, if <file> exists and has <strong>sgid bit</strong> set.</td> </tr> <tr> <td><strong>-u</strong> <file></td> <td>True, if <file> exists and has <strong>suid bit</strong> set.</td> </tr> <tr> <td><strong>-r</strong> <file></td> <td>True, if <file> exists and is <strong>readable</strong>.</td> </tr> <tr> <td><strong>-w</strong> <file></td> <td>True, if <file> exists and is <strong>writable</strong>.</td> </tr> <tr> <td><strong>-x</strong> <file></td> <td>True, if <file> exists and is <strong>executable</strong>.</td> </tr> <tr> <td><strong>-s</strong> <file></td> <td>True, if <file> exists and has size bigger than 0 (<strong>not empty</strong>).</td> </tr> <tr> <td><strong>-t</strong> <fd></td> <td>True, if file descriptor <fd> is open and refers to a terminal.</td> </tr> <tr> <td><file1> <strong>-nt</strong> <file2></td> <td>True, if <file1> is <strong>newer than</strong> <file2> (mtime). <img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/26a0.svg title=:warning:></td> </tr> <tr> <td><file1> <strong>-ot</strong> <file2></td> <td>True, if <file1> is <strong>older than</strong> <file2> (mtime). <img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/26a0.svg title=:warning:></td> </tr> <tr> <td><file1> <strong>-ef</strong> <file2></td> <td>True, if <file1> and <file2> refer to the <strong>same device and inode numbers</strong>. <img alt=⚠ class=twemoji src=https://cdn.jsd
|
|||
|
<a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-n<span class=w> </span><span class=nv>$var</span><span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"var is not empty"</span><span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>This code prints "var is not empty", even though <code>-n something</code> is supposed to be true if <code>$var</code> is not empty - <strong>why?</strong></p> <p>Here, as <code>$var</code> is <strong>not quoted</strong>, word splitting occurs and <code>$var</code> results in actually nothing (Bash removes it from the command's argument list!). So the test is in fact <code>[ -n ]</code> <strong>and falls into the "one argument" rule</strong>, the only argument is "-n" which is not null and so the test returns true. The solution, as usual, is to <strong>quote the parameter expansion</strong>: <code>[ -n "$var" ]</code> so that the test has always 2 arguments, even if the second one is the null string.</p> <p>These rules also explain why, for instance, -a and -o can have several meanings.</p> <h2 id=and-and-or>AND and OR<a class=headerlink href=#and-and-or title="Permanent link">¶</a></h2> <h3 id=the-prefered-way>The Prefered Way<a class=headerlink href=#the-prefered-way title="Permanent link">¶</a></h3> <p>The way often recommended to logically connect several tests with AND and OR is to use <strong>several single test commands</strong> and to <strong>combine</strong> them with the shell <code>&&</code> and <code>||</code> <strong>list control operators</strong>.</p> <p>See this:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-n<span class=w> </span><span class=s2>"</span><span class=nv>$var</span><span class=s2>"</span><span class=o>]</span><span class=w> </span><span class=o>&&</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span><span class=s2>"</span><span class=nv>$var</span><span class=s2>"</span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span>
|
|||
|
<a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"\$var is not null and a file named </span><span class=nv>$var</span><span class=s2> exists!"</span>
|
|||
|
<a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>The return status of AND and OR lists is the exit status of the last command executed in the list</p> <ul> <li>With <code>command1 && command2</code>, <code>command2</code> is executed if, and only if, <code>command1</code> returns an exit status of zero (true)</li> <li>With <code>command1 ││ command2</code>, <code>command2</code> is executed if, and only if, <code>command1</code> returns a non-zero exit status (false)</li> </ul> <h3 id=the-other-way-a-and-o>The other way: -a and -o<a class=headerlink href=#the-other-way-a-and-o title="Permanent link">¶</a></h3> <p>The logical operators AND and OR for the test-command itself are <code>-a</code> and <code>-o</code>, thus:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-n<span class=w> </span><span class=s2>"</span><span class=nv>$var</span><span class=s2>"</span><span class=w> </span>-a<span class=w> </span>-e<span class=w> </span><span class=s2>"</span><span class=nv>$var</span><span class=s2>"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=p>;</span><span class=w> </span><span class=k>then</span>
|
|||
|
<a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"\$var is not null and a file named </span><span class=nv>$var</span><span class=s2> exists"</span>
|
|||
|
<a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>They are <strong>not</strong> <code>&&</code> or <code>||</code>:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-n<span class=w> </span><span class=s2>"/tmp"</span><span class=w> </span><span class=o>&&</span><span class=w> </span>-d<span class=w> </span><span class=s2>"/tmp"</span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>fi</span><span class=w> </span><span class=c1># DOES NOT WORK</span>
|
|||
|
<a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a>bash:<span class=w> </span><span class=o>[</span>:<span class=w> </span>missing<span class=w> </span><span class=sb>`</span><span class=o>]</span><span class=err>'</span>
|
|||
|
</code></pre></div> <p>You might find the error message confusing, <code>[</code> does not find the required final <code>]</code>, because as seen above <code>&&</code> is used to write a <strong>list of commands</strong>. The <code>if</code> statement actually <strong>sees two commands</strong>:</p> <ul> <li><code>[ -n "/tmp"</code></li> <li><code>-d "/tmp" ]</code></li> </ul> <p>...which <strong>must</strong> fail.</p> <h3 id=why-you-should-avoid-using-a-and-o>Why you should avoid using -a and -o<a class=headerlink href=#why-you-should-avoid-using-a-and-o title="Permanent link">¶</a></h3> <h4 id=if-portability-is-a-concern>If portability is a concern<a class=headerlink href=#if-portability-is-a-concern title="Permanent link">¶</a></h4> <p>POSIX®/SUSv3 does <strong>not</strong> specify the behaviour of <code>test</code> in cases where there are more than 4 arguments. If you write a script that might not be executed by Bash, the behaviour might be different! <sup id=fnref:1><a class=footnote-ref href=#fn:1>1</a></sup></p> <h4 id=if-you-want-the-cut-behaviour>If you want the cut behaviour<a class=headerlink href=#if-you-want-the-cut-behaviour title="Permanent link">¶</a></h4> <p>Let's say, we want to check the following two things (AND):</p> <ol> <li>if a string is null (empty)</li> <li>if a command produced an output</li> </ol> <p>Let's see:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-z<span class=w> </span><span class=s2>"false"</span><span class=w> </span>-a<span class=w> </span>-z<span class=w> </span><span class=s2>"</span><span class=k>$(</span><span class=nb>echo</span><span class=w> </span>I<span class=w> </span>am<span class=w> </span>executed<span class=w> </span>><span class=p>&</span><span class=m>2</span><span class=k>)</span><span class=s2>"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span>...<span class=w> </span>
|
|||
|
</code></pre></div> <p>=> The arguments are all expanded <strong>before</strong> <code>test</code> runs, thus the echo-command <strong>is executed</strong>.</p> <div class=highlight><pre><span></span><code><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span>-z<span class=w> </span><span class=s2>"false"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>&&</span><span class=w> </span><span class=o>[</span><span class=w> </span>-z<span class=w> </span><span class=s2>"</span><span class=k>$(</span><span class=nb>echo</span><span class=w> </span>I<span class=w> </span>am<span class=w> </span>not<span class=w> </span>executed<span class=w> </span>><span class=p>&</span><span class=m>2</span><span class=k>)</span><span class=s2>"</span><span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span>...<span class=w> </span>
|
|||
|
</code></pre></div> <p>=> Due to the nature of the <code>&&</code> list operator, the second test-command runs only if the first test-command returns true, our echo-command <strong>is not executed</strong>.</p> <div class="admonition note"> <p class=admonition-title>Note</p> <p>In my opinion, <code>-a</code> and <code>-o</code> are also less readable <code>[pgas]</code></p> </div> <h3 id=precedence-and-parenthesis>Precedence and Parenthesis<a class=headerlink href=#precedence-and-parenthesis title="Permanent link">¶</a></h3> <p>Take care if you convert your scripts from using <code>-a</code> and <code>-o</code> to use the list way (<code>&&</code> and <code>||</code>):</p> <ul> <li>in the test-command rules, <code>-a</code> has <strong>precedence over</strong> <code>-o</code></li> <li>in the shell grammar rules, <code>&&</code> and <code>||</code> have <strong>equal precedence</strong></li> </ul> <p>That means, <strong>you can get different results</strong>, depending on the manner of use:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>||</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>&&</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>false<span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
<a id=__codelineno-10-2 name=__codelineno-10-2 href=#__codelineno-10-2></a><span class=nb>false</span>
|
|||
|
<a id=__codelineno-10-3 name=__codelineno-10-3 href=#__codelineno-10-3></a>
|
|||
|
<a id=__codelineno-10-4 name=__codelineno-10-4 href=#__codelineno-10-4></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span>-o<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span>-a<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>false<span class=p>;</span><span class=k>fi</span>
|
|||
|
<a id=__codelineno-10-5 name=__codelineno-10-5 href=#__codelineno-10-5></a><span class=nb>true</span>
|
|||
|
</code></pre></div> <p>As a result you have to think about it a little or add precedence control (parenthesis).</p> <p>For <code>&&</code> and <code>||</code> parenthesis means (shell-ly) grouping the commands, and since <code>( ... )</code> introduces a subshell we will use <code>{ ... }</code> instead:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>||</span><span class=w> </span><span class=o>{</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>&&</span><span class=w> </span><span class=o>[</span><span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=w> </span><span class=p>;</span><span class=o>}</span><span class=w> </span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>false<span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
<a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a><span class=nb>true</span>
|
|||
|
</code></pre></div> <p>For the test command, the precedence parenthesis are, as well, <code>( )</code>, but you need to escape or quote them, so that the shell doesn't try to interpret them:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span><span class=o>(</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span>-o<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>)</span><span class=w> </span>-a<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>false<span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
<a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=nb>false</span>
|
|||
|
<a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a>
|
|||
|
<a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=c1># equivalent, but less readable IMHO:</span>
|
|||
|
<a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a>$<span class=w> </span><span class=k>if</span><span class=w> </span><span class=o>[</span><span class=w> </span><span class=s1>'('</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span>-o<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=s1>')'</span><span class=w> </span>-a<span class=w> </span>-e<span class=w> </span>/does/not/exist<span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>true<span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span>false<span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
<a id=__codelineno-12-6 name=__codelineno-12-6 href=#__codelineno-12-6></a><span class=nb>false</span>
|
|||
|
</code></pre></div> <h2 id=not>NOT<a class=headerlink href=#not title="Permanent link">¶</a></h2> <p>As for AND and OR, there are 2 ways to negate a test with the shell keyword <code>!</code> or passing <code>!</code> as an argument to <code>test</code>.</p> <p>Here <code>!</code> negates the exit status of the command <code>test</code> which is 0 (true), and the else part is executed:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-13-1 name=__codelineno-13-1 href=#__codelineno-13-1></a><span class=k>if</span><span class=w> </span>!<span class=w> </span><span class=o>[</span><span class=w> </span>-d<span class=w> </span><span class=s1>'/tmp'</span><span class=w> </span><span class=o>]</span><span class=p>;</span><span class=w> </span><span class=k>then</span><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"/tmp doesn't exists"</span><span class=p>;</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"/tmp exists"</span><span class=p>;</span><span class=w> </span><span class=k>fi</span>
|
|||
|
</code></pre></div> <p>Here the <code>test</code> command itself exits with status 1 (false) and the else is also executed:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-14-1 name=__codelineno-14-1 href=#__codelineno-14-1></a>if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
|||
|
</code></pre></div> <p>Unlike for AND and OR, both methods for NOT have an identical behaviour, at least for doing one single test.</p> <h2 id=pitfalls-summarized>Pitfalls summarized<a class=headerlink href=#pitfalls-summarized title="Permanent link">¶</a></h2> <p>In this section you will get all the mentioned (and maybe more) possible pitfalls and problems in a summary.</p> <h3 id=general>General<a class=headerlink href=#general title="Permanent link">¶</a></h3> <p>Here's the copy of a mail on bug-bash list. A user asking a question about using the test command in Bash, <strong>he's talking about a problem, which you may have already had yourself</strong>:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-15-1 name=__codelineno-15-1 href=#__codelineno-15-1></a>From: (PROTECTED)
|
|||
|
<a id=__codelineno-15-2 name=__codelineno-15-2 href=#__codelineno-15-2></a>Subject: -d option not working. . .?
|
|||
|
<a id=__codelineno-15-3 name=__codelineno-15-3 href=#__codelineno-15-3></a>Date: Tue, 11 Sep 2007 21:51:59 -0400
|
|||
|
<a id=__codelineno-15-4 name=__codelineno-15-4 href=#__codelineno-15-4></a>To: bug-bash@gnu.org
|
|||
|
<a id=__codelineno-15-5 name=__codelineno-15-5 href=#__codelineno-15-5></a>
|
|||
|
<a id=__codelineno-15-6 name=__codelineno-15-6 href=#__codelineno-15-6></a>Hi All,
|
|||
|
<a id=__codelineno-15-7 name=__codelineno-15-7 href=#__codelineno-15-7></a>
|
|||
|
<a id=__codelineno-15-8 name=__codelineno-15-8 href=#__codelineno-15-8></a>I've got a script that I'm trying to set up, but it keeps telling me
|
|||
|
<a id=__codelineno-15-9 name=__codelineno-15-9 href=#__codelineno-15-9></a>that "[-d command not found". Can someone please explain what is
|
|||
|
<a id=__codelineno-15-10 name=__codelineno-15-10 href=#__codelineno-15-10></a>wrong with this?:
|
|||
|
<a id=__codelineno-15-11 name=__codelineno-15-11 href=#__codelineno-15-11></a>
|
|||
|
<a id=__codelineno-15-12 name=__codelineno-15-12 href=#__codelineno-15-12></a>
|
|||
|
<a id=__codelineno-15-13 name=__codelineno-15-13 href=#__codelineno-15-13></a>
|
|||
|
<a id=__codelineno-15-14 name=__codelineno-15-14 href=#__codelineno-15-14></a>
|
|||
|
<a id=__codelineno-15-15 name=__codelineno-15-15 href=#__codelineno-15-15></a>#!/bin/sh
|
|||
|
<a id=__codelineno-15-16 name=__codelineno-15-16 href=#__codelineno-15-16></a>
|
|||
|
<a id=__codelineno-15-17 name=__codelineno-15-17 href=#__codelineno-15-17></a>for i in $*
|
|||
|
<a id=__codelineno-15-18 name=__codelineno-15-18 href=#__codelineno-15-18></a>do
|
|||
|
<a id=__codelineno-15-19 name=__codelineno-15-19 href=#__codelineno-15-19></a>{
|
|||
|
<a id=__codelineno-15-20 name=__codelineno-15-20 href=#__codelineno-15-20></a> if [-d $i]
|
|||
|
<a id=__codelineno-15-21 name=__codelineno-15-21 href=#__codelineno-15-21></a> then
|
|||
|
<a id=__codelineno-15-22 name=__codelineno-15-22 href=#__codelineno-15-22></a> echo "$i is a directory! Yay!"
|
|||
|
<a id=__codelineno-15-23 name=__codelineno-15-23 href=#__codelineno-15-23></a> else
|
|||
|
<a id=__codelineno-15-24 name=__codelineno-15-24 href=#__codelineno-15-24></a> echo "$i is not a directory!"
|
|||
|
<a id=__codelineno-15-25 name=__codelineno-15-25 href=#__codelineno-15-25></a> fi
|
|||
|
<a id=__codelineno-15-26 name=__codelineno-15-26 href=#__codelineno-15-26></a>}
|
|||
|
<a id=__codelineno-15-27 name=__codelineno-15-27 href=#__codelineno-15-27></a>done
|
|||
|
<a id=__codelineno-15-28 name=__codelineno-15-28 href=#__codelineno-15-28></a>
|
|||
|
<a id=__codelineno-15-29 name=__codelineno-15-29 href=#__codelineno-15-29></a>Regards
|
|||
|
</code></pre></div> <p>See the problem regarding the used test-command (the other potential problems are not of interest here)?</p> <div class=highlight><pre><span></span><code><a id=__codelineno-16-1 name=__codelineno-16-1 href=#__codelineno-16-1></a><span class=o>[</span>-d<span class=w> </span><span class=nv>$i</span><span class=o>]</span>
|
|||
|
</code></pre></div> <p>He simply didn't know that <code>test</code> or <code>[</code> is a normal, simple command. Well, here's the answer he got. I quote it here, because it's a well written text that addresses most of the common issues with the "classic" test command:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-17-1 name=__codelineno-17-1 href=#__codelineno-17-1></a>From: Bob Proulx (EMAIL PROTECTED)
|
|||
|
<a id=__codelineno-17-2 name=__codelineno-17-2 href=#__codelineno-17-2></a>Subject: Re: -d option not working. . .?
|
|||
|
<a id=__codelineno-17-3 name=__codelineno-17-3 href=#__codelineno-17-3></a>Date: Wed, 12 Sep 2007 10:32:35 -0600
|
|||
|
<a id=__codelineno-17-4 name=__codelineno-17-4 href=#__codelineno-17-4></a>To: bug-bash@gnu.org
|
|||
|
<a id=__codelineno-17-5 name=__codelineno-17-5 href=#__codelineno-17-5></a>
|
|||
|
<a id=__codelineno-17-6 name=__codelineno-17-6 href=#__codelineno-17-6></a>> (QUOTED TEXT WAS REMOVED)
|
|||
|
<a id=__codelineno-17-7 name=__codelineno-17-7 href=#__codelineno-17-7></a>
|
|||
|
<a id=__codelineno-17-8 name=__codelineno-17-8 href=#__codelineno-17-8></a>The shell is first and foremost a way to launch other commands. The
|
|||
|
<a id=__codelineno-17-9 name=__codelineno-17-9 href=#__codelineno-17-9></a>syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
|
|||
|
<a id=__codelineno-17-10 name=__codelineno-17-10 href=#__codelineno-17-10></a>or even if cmd1; cmd2; cmd3; then). Plus the '( ... )' syntax is
|
|||
|
<a id=__codelineno-17-11 name=__codelineno-17-11 href=#__codelineno-17-11></a>already taken by the use of starting a subshell.
|
|||
|
<a id=__codelineno-17-12 name=__codelineno-17-12 href=#__codelineno-17-12></a>
|
|||
|
<a id=__codelineno-17-13 name=__codelineno-17-13 href=#__codelineno-17-13></a>As I recall in the original shell language the file test operator was
|
|||
|
<a id=__codelineno-17-14 name=__codelineno-17-14 href=#__codelineno-17-14></a>not built-in. It was provided by the standalone '/bin/test' command.
|
|||
|
<a id=__codelineno-17-15 name=__codelineno-17-15 href=#__codelineno-17-15></a>The result was effectively this:
|
|||
|
<a id=__codelineno-17-16 name=__codelineno-17-16 href=#__codelineno-17-16></a>
|
|||
|
<a id=__codelineno-17-17 name=__codelineno-17-17 href=#__codelineno-17-17></a> if /bin/test -d somedir
|
|||
|
<a id=__codelineno-17-18 name=__codelineno-17-18 href=#__codelineno-17-18></a>
|
|||
|
<a id=__codelineno-17-19 name=__codelineno-17-19 href=#__codelineno-17-19></a>Although the full path /bin/test was never used. I showed it that way
|
|||
|
<a id=__codelineno-17-20 name=__codelineno-17-20 href=#__codelineno-17-20></a>here for emphasis that following the 'if' statement is a command list.
|
|||
|
<a id=__codelineno-17-21 name=__codelineno-17-21 href=#__codelineno-17-21></a>Normally it would simply have been:
|
|||
|
<a id=__codelineno-17-22 name=__codelineno-17-22 href=#__codelineno-17-22></a>
|
|||
|
<a id=__codelineno-17-23 name=__codelineno-17-23 href=#__codelineno-17-23></a> if test -d somedir
|
|||
|
<a id=__codelineno-17-24 name=__codelineno-17-24 href=#__codelineno-17-24></a>
|
|||
|
<a id=__codelineno-17-25 name=__codelineno-17-25 href=#__codelineno-17-25></a>Of course that is fine and for the best portability that style is
|
|||
|
<a id=__codelineno-17-26 name=__codelineno-17-26 href=#__codelineno-17-26></a>still the recommended way today to use the test command. But many
|
|||
|
<a id=__codelineno-17-27 name=__codelineno-17-27 href=#__codelineno-17-27></a>people find that it looks different from other programming languages.
|
|||
|
<a id=__codelineno-17-28 name=__codelineno-17-28 href=#__codelineno-17-28></a>To make the test operator (note I mention the test operator and not
|
|||
|
<a id=__codelineno-17-29 name=__codelineno-17-29 href=#__codelineno-17-29></a>the shell language, this is a localized change not affecting the
|
|||
|
<a id=__codelineno-17-30 name=__codelineno-17-30 href=#__codelineno-17-30></a>language as a whole) look more like other programming languages the
|
|||
|
<a id=__codelineno-17-31 name=__codelineno-17-31 href=#__codelineno-17-31></a>'test' program was coded to ignore the last argument if it was a ']'.
|
|||
|
<a id=__codelineno-17-32 name=__codelineno-17-32 href=#__codelineno-17-32></a>Then a copy of the test program could be used as the '[' program.
|
|||
|
<a id=__codelineno-17-33 name=__codelineno-17-33 href=#__codelineno-17-33></a>
|
|||
|
<a id=__codelineno-17-34 name=__codelineno-17-34 href=#__codelineno-17-34></a> ...modify /bin/test to ignore ']' as last argument...
|
|||
|
<a id=__codelineno-17-35 name=__codelineno-17-35 href=#__codelineno-17-35></a> cp /bin/test /bin/[
|
|||
|
<a id=__codelineno-17-36 name=__codelineno-17-36 href=#__codelineno-17-36></a>
|
|||
|
<a id=__codelineno-17-37 name=__codelineno-17-37 href=#__codelineno-17-37></a>This allows:
|
|||
|
<a id=__codelineno-17-38 name=__codelineno-17-38 href=#__codelineno-17-38></a>
|
|||
|
<a id=__codelineno-17-39 name=__codelineno-17-39 href=#__codelineno-17-39></a> if [ -d somedir ]
|
|||
|
<a id=__codelineno-17-40 name=__codelineno-17-40 href=#__codelineno-17-40></a>
|
|||
|
<a id=__codelineno-17-41 name=__codelineno-17-41 href=#__codelineno-17-41></a>Doesn't that look more normal? People liked it and it caught on. It
|
|||
|
<a id=__codelineno-17-42 name=__codelineno-17-42 href=#__codelineno-17-42></a>was so popular that both 'test' and '[' are now shell built-ins. They
|
|||
|
<a id=__codelineno-17-43 name=__codelineno-17-43 href=#__codelineno-17-43></a>don't launch an external '/bin/test' program anymore. But they *used*
|
|||
|
<a id=__codelineno-17-44 name=__codelineno-17-44 href=#__codelineno-17-44></a>to launch external programs. Therefore argument parsing is the same
|
|||
|
<a id=__codelineno-17-45 name=__codelineno-17-45 href=#__codelineno-17-45></a>as if they still did launch an external program. This affects
|
|||
|
<a id=__codelineno-17-46 name=__codelineno-17-46 href=#__codelineno-17-46></a>argument parsing.
|
|||
|
<a id=__codelineno-17-47 name=__codelineno-17-47 href=#__codelineno-17-47></a>
|
|||
|
<a id=__codelineno-17-48 name=__codelineno-17-48 href=#__codelineno-17-48></a> it test -f *.txt
|
|||
|
<a id=__codelineno-17-49 name=__codelineno-17-49 href=#__codelineno-17-49></a> test: too many arguments
|
|||
|
<a id=__codelineno-17-50 name=__codelineno-17-50 href=#__codelineno-17-50></a>
|
|||
|
<a id=__codelineno-17-51 name=__codelineno-17-51 href=#__codelineno-17-51></a>Oops. I have twenty .txt files and so test got one -f followed by the
|
|||
|
<a id=__codelineno-17-52 name=__codelineno-17-52 href=#__codelineno-17-52></a>first file followed by the remaining files. (e.g. test -f 1.txt 2.txt
|
|||
|
<a id=__codelineno-17-53 name=__codelineno-17-53 href=#__codelineno-17-53></a>3.txt 4.txt)
|
|||
|
<a id=__codelineno-17-54 name=__codelineno-17-54 href=#__codelineno-17-54></a>
|
|||
|
<a id=__codelineno-17-55 name=__codelineno-17-55 href=#__codelineno-17-55></a> if test -d $file
|
|||
|
<a id=__codelineno-17-56 name=__codelineno-17-56 href=#__codelineno-17-56></a> test: argument expected
|
|||
|
<a id=__codelineno-17-57 name=__codelineno-17-57 href=#__codelineno-17-57></a>
|
|||
|
<a id=__codelineno-17-58 name=__codelineno-17-58 href=#__codelineno-17-58></a>Oops. I meant to set file.
|
|||
|
<a id=__codelineno-17-59 name=__codelineno-17-59 href=#__codelineno-17-59></a>
|
|||
|
<a id=__codelineno-17-60 name=__codelineno-17-60 href=#__codelineno-17-60></a> file=/path/some/file
|
|||
|
<a id=__codelineno-17-61 name=__codelineno-17-61 href=#__codelineno-17-61></a> if test -d $file
|
|||
|
<a id=__codelineno-17-62 name=__codelineno-17-62 href=#__codelineno-17-62></a>
|
|||
|
<a id=__codelineno-17-63 name=__codelineno-17-63 href=#__codelineno-17-63></a>If variables such as that are not set then they wlll be expanded by
|
|||
|
<a id=__codelineno-17-64 name=__codelineno-17-64 href=#__codelineno-17-64></a>the shell before passing them to the (possibly external) command and
|
|||
|
<a id=__codelineno-17-65 name=__codelineno-17-65 href=#__codelineno-17-65></a>disappear entirely. This is why test arguments should always be quoted.
|
|||
|
<a id=__codelineno-17-66 name=__codelineno-17-66 href=#__codelineno-17-66></a>
|
|||
|
<a id=__codelineno-17-67 name=__codelineno-17-67 href=#__codelineno-17-67></a> if test -d "$file"
|
|||
|
<a id=__codelineno-17-68 name=__codelineno-17-68 href=#__codelineno-17-68></a> if [ -d "$file" ]
|
|||
|
<a id=__codelineno-17-69 name=__codelineno-17-69 href=#__codelineno-17-69></a>
|
|||
|
<a id=__codelineno-17-70 name=__codelineno-17-70 href=#__codelineno-17-70></a>Actually today test is defined that if only one argument is given as
|
|||
|
<a id=__codelineno-17-71 name=__codelineno-17-71 href=#__codelineno-17-71></a>in this case "test FOO" then then test returns true if the argument is
|
|||
|
<a id=__codelineno-17-72 name=__codelineno-17-72 href=#__codelineno-17-72></a>non-zero in text length. Because "-d" is non-zero length "test -d" is
|
|||
|
<a id=__codelineno-17-73 name=__codelineno-17-73 href=#__codelineno-17-73></a>true. The number of arguments affects how test parses the args. This
|
|||
|
<a id=__codelineno-17-74 name=__codelineno-17-74 href=#__codelineno-17-74></a>avoids a case where depending upon the data may look like a test
|
|||
|
<a id=__codelineno-17-75 name=__codelineno-17-75 href=#__codelineno-17-75></a>operator.
|
|||
|
<a id=__codelineno-17-76 name=__codelineno-17-76 href=#__codelineno-17-76></a>
|
|||
|
<a id=__codelineno-17-77 name=__codelineno-17-77 href=#__codelineno-17-77></a> DATA="something"
|
|||
|
<a id=__codelineno-17-78 name=__codelineno-17-78 href=#__codelineno-17-78></a> if test "$DATA" # true, $DATA is non-zero length
|
|||
|
<a id=__codelineno-17-79 name=__codelineno-17-79 href=#__codelineno-17-79></a>
|
|||
|
<a id=__codelineno-17-80 name=__codelineno-17-80 href=#__codelineno-17-80></a> DATA=""
|
|||
|
<a id=__codelineno-17-81 name=__codelineno-17-81 href=#__codelineno-17-81></a> if test "$DATA" # false, $DATA is zero length
|
|||
|
<a id=__codelineno-17-82 name=__codelineno-17-82 href=#__codelineno-17-82></a>
|
|||
|
<a id=__codelineno-17-83 name=__codelineno-17-83 href=#__codelineno-17-83></a>But the problem case is how should test handle an argument that looks
|
|||
|
<a id=__codelineno-17-84 name=__codelineno-17-84 href=#__codelineno-17-84></a>like an operator? This used to generate errors but now because it is
|
|||
|
<a id=__codelineno-17-85 name=__codelineno-17-85 href=#__codelineno-17-85></a>only one argument is defined to be the same as test -n $DATA.
|
|||
|
<a id=__codelineno-17-86 name=__codelineno-17-86 href=#__codelineno-17-86></a>
|
|||
|
<a id=__codelineno-17-87 name=__codelineno-17-87 href=#__codelineno-17-87></a> DATA="-d"
|
|||
|
<a id=__codelineno-17-88 name=__codelineno-17-88 href=#__codelineno-17-88></a> if test "$DATA" # true, $DATA is non-zero length
|
|||
|
<a id=__codelineno-17-89 name=__codelineno-17-89 href=#__codelineno-17-89></a> if test -d # true, same as previous case.
|
|||
|
<a id=__codelineno-17-90 name=__codelineno-17-90 href=#__codelineno-17-90></a>
|
|||
|
<a id=__codelineno-17-91 name=__codelineno-17-91 href=#__codelineno-17-91></a>Because test and [ are possibly external commands all of the parts of
|
|||
|
<a id=__codelineno-17-92 name=__codelineno-17-92 href=#__codelineno-17-92></a>them are chosen to avoid shell metacharacters. The Fortran operator
|
|||
|
<a id=__codelineno-17-93 name=__codelineno-17-93 href=#__codelineno-17-93></a>naming was well known at the time (e.g. .gt., .eq., etc.) and was
|
|||
|
<a id=__codelineno-17-94 name=__codelineno-17-94 href=#__codelineno-17-94></a>pressed into service for the shell test operator too. Comming from
|
|||
|
<a id=__codelineno-17-95 name=__codelineno-17-95 href=#__codelineno-17-95></a>Fortran using -gt, -eq, etc. looked very normal.
|
|||
|
<a id=__codelineno-17-96 name=__codelineno-17-96 href=#__codelineno-17-96></a>
|
|||
|
<a id=__codelineno-17-97 name=__codelineno-17-97 href=#__codelineno-17-97></a>Incorrect use generating unlikely to be intended results:
|
|||
|
<a id=__codelineno-17-98 name=__codelineno-17-98 href=#__codelineno-17-98></a>
|
|||
|
<a id=__codelineno-17-99 name=__codelineno-17-99 href=#__codelineno-17-99></a> if test 5 > 2 # true, "5" is non-zero length, creates file named "2"
|
|||
|
<a id=__codelineno-17-100 name=__codelineno-17-100 href=#__codelineno-17-100></a>
|
|||
|
<a id=__codelineno-17-101 name=__codelineno-17-101 href=#__codelineno-17-101></a>Intended use:
|
|||
|
<a id=__codelineno-17-102 name=__codelineno-17-102 href=#__codelineno-17-102></a>
|
|||
|
<a id=__codelineno-17-103 name=__codelineno-17-103 href=#__codelineno-17-103></a> if test 5 -gt 2 # true (and no shell meta characters needing quoting)
|
|||
|
<a id=__codelineno-17-104 name=__codelineno-17-104 href=#__codelineno-17-104></a>
|
|||
|
<a id=__codelineno-17-105 name=__codelineno-17-105 href=#__codelineno-17-105></a>Then much later, sometime in the mid 1980's, the Korn sh decided to
|
|||
|
<a id=__codelineno-17-106 name=__codelineno-17-106 href=#__codelineno-17-106></a>improve upon this situation. A new test operator was introduced.
|
|||
|
<a id=__codelineno-17-107 name=__codelineno-17-107 href=#__codelineno-17-107></a>This one was always a shell built-in and therefore could act upon the
|
|||
|
<a id=__codelineno-17-108 name=__codelineno-17-108 href=#__codelineno-17-108></a>shell arguments directly. This is '[[' which is a shell keyword.
|
|||
|
<a id=__codelineno-17-109 name=__codelineno-17-109 href=#__codelineno-17-109></a>(Keyword, metacharacters, builtins, all are different.) Because the
|
|||
|
<a id=__codelineno-17-110 name=__codelineno-17-110 href=#__codelineno-17-110></a>shell processes [[ internally all arguments are known and do not need
|
|||
|
<a id=__codelineno-17-111 name=__codelineno-17-111 href=#__codelineno-17-111></a>to be quoted.
|
|||
|
<a id=__codelineno-17-112 name=__codelineno-17-112 href=#__codelineno-17-112></a>
|
|||
|
<a id=__codelineno-17-113 name=__codelineno-17-113 href=#__codelineno-17-113></a> if [[ -d $file ]] # okay
|
|||
|
<a id=__codelineno-17-114 name=__codelineno-17-114 href=#__codelineno-17-114></a> if [[ 5 > 2 ]] # okay
|
|||
|
<a id=__codelineno-17-115 name=__codelineno-17-115 href=#__codelineno-17-115></a>
|
|||
|
<a id=__codelineno-17-116 name=__codelineno-17-116 href=#__codelineno-17-116></a>I am sure that I am remembering a detail wrong but hopefully this is
|
|||
|
<a id=__codelineno-17-117 name=__codelineno-17-117 href=#__codelineno-17-117></a>useful as a gentle introduction and interesting anyway.
|
|||
|
<a id=__codelineno-17-118 name=__codelineno-17-118 href=#__codelineno-17-118></a>
|
|||
|
<a id=__codelineno-17-119 name=__codelineno-17-119 href=#__codelineno-17-119></a>Bob
|
|||
|
</code></pre></div> <p>I hope this text protects you a bit from stepping from one pitfall into the next.</p> <p>I find it very interesting and informative, that's why I quoted it here. Many thanks, Bob, also for the permission to copy the text here!</p> <h2 id=code-examples>Code examples<a class=headerlink href=#code-examples title="Permanent link">¶</a></h2> <h3 id=snipplets>Snipplets<a class=headerlink href=#snipplets title="Permanent link">¶</a></h3> <p>Some code snipplets follow, different ways of shell reaction is used.</p> <ul> <li><strong>check if a variable is defined/non-NULL</strong><ul> <li><code>test "$MYVAR"</code></li> <li><code>[ "$MYVAR" ]</code></li> <li><strong>Note:</strong> There are possibilities to make a difference if a variable is <em>undefined</em> or <em>NULL</em> - see <a href=../../syntax/pe/#use_an_alternate_value>Parameter Expansion - Using an alternate value</a></li> </ul> </li> <li><strong>check if a directory exists, if not, create it</strong><ul> <li><code>test ! -d /home/user/foo && mkdir /home/user/foo</code></li> <li><code>[ ! -d /home/user/foo ] && mkdir /home/user/foo</code></li> <li><code>if [ ! -d /home/user/foo ]; then mkdir /home/user/foo; fi</code></li> </ul> </li> <li><strong>check if minimum one parameter was given, and that one is "Hello"</strong><ul> <li><code>test $# -ge 1 -a "$1" = "Hello" || exit 1</code></li> <li><code>[ $# -ge 1 ] && [ "$1" = "Hello" ] || exit 1</code> (see <a href=../../syntax/basicgrammar/#lists>lists description</a>)</li> </ul> </li> </ul> <h3 id=listing-directories>Listing directories<a class=headerlink href=#listing-directories title="Permanent link">¶</a></h3> <p>Using a <a href=../../syntax/ccmd/classic_for/ >for-loop</a> to iterate through all entries of a directory, if an entry is a directory (<code>[ -d "$fn" ]</code>), print its name:</p> <div class=highlight><pre><span></span><code><a id=__codelineno-18-1 name=__codelineno-18-1 href=#__codelineno-18-1></a><span class=k>for</span><span class=w> </span>fn<span class=w> </span><span class=k>in</span><span class=w> </span>*<span class=p>;</span><span class=w> </span><span class=k>do</span>
|
|||
|
<a id=__codelineno-18-2 name=__codelineno-18-2 href=#__codelineno-18-2></a><span class=w> </span><span class=o>[</span><span class=w> </span>-d<span class=w> </span><span class=s2>"</span><span class=nv>$fn</span><span class=s2>"</span><span class=w> </span><span class=o>]</span><span class=w> </span><span class=o>&&</span><span class=w> </span><span class=nb>echo</span><span class=w> </span><span class=s2>"</span><span class=nv>$fn</span><span class=s2>"</span>
|
|||
|
<a id=__codelineno-18-3 name=__codelineno-18-3 href=#__codelineno-18-3></a><span class=k>done</span>
|
|||
|
</code></pre></div> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">¶</a></h2> <ul> <li>Internal: <a href=../../syntax/ccmd/conditional_expression/ >conditional expression</a> (aka "the new test command")</li> <li>Internal: <a href=../../syntax/ccmd/if_clause/ >the if-clause</a></li> </ul> <div class=footnote> <hr> <ol> <li id=fn:1> <p><rant>Of course, one can wonder what is the use of including the parenthesis in the specification without defining the behaviour with more than 4 arguments or how usefull are the examples with 7 or 9 arguments attached to the specification.</rant> <a class=footnote-backref href=#fnref:1 title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div> <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 7, 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 7, 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>
|