<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Path · XB Field Notes</title><link>https://www.xbcatm.com/tags/path/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.xbcatm.com/tags/path/index.xml" rel="self" type="application/rss+xml"/><item><title>A bare command the shell cannot find</title><link>https://www.xbcatm.com/posts/shell-command-not-found-path-search/</link><pubDate>Thu, 20 May 2027 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/posts/shell-command-not-found-path-search/</guid><description>Command lookup depends on functions, builtins, cached paths, and PATH order. A slash bypasses the search; execution failures need separate diagnosis.</description><content:encoded>&lt;p&gt;“Command not found” identifies a lookup failure, but lookup does not always mean walking every directory in &lt;code&gt;PATH&lt;/code&gt;. In ordinary Bash, a command word without a slash can resolve to a function, builtin, cached executable path, or a newly searched executable. An alias may already have changed the command while the input was read. Inspect the shell that actually reported the failure before reinstalling a tool that may already exist.&lt;/p&gt;
&lt;h2 id="know-which-lookup-layer-answered"&gt;Know which lookup layer answered&lt;/h2&gt;
&lt;p&gt;After expansion, Bash checks functions and then builtins before searching for an external command. &lt;code&gt;type name&lt;/code&gt; describes the current resolution, and &lt;code&gt;type -a name&lt;/code&gt; exposes additional definitions or candidates. &lt;code&gt;command -v name&lt;/code&gt; is useful for testing availability in that shell, but its result need not be a filesystem path. It can identify a function or builtin, so do not blindly treat the output as a file to copy.&lt;/p&gt;
&lt;p&gt;For external commands, directories in &lt;code&gt;PATH&lt;/code&gt; are searched in order. An earlier executable can shadow a newer version later in the list. Bash also caches successful paths; &lt;code&gt;hash -t name&lt;/code&gt; inspects a cached entry and &lt;code&gt;hash -r&lt;/code&gt; clears the table. A changed file at an already cached location and a different path choice are separate issues. Starting a new terminal can hide this distinction by resetting shell state.&lt;/p&gt;
&lt;h2 id="a-slash-selects-a-path-directly"&gt;A slash selects a path directly&lt;/h2&gt;
&lt;p&gt;Any slash in the command word bypasses the external &lt;code&gt;PATH&lt;/code&gt; search. &lt;code&gt;./tool&lt;/code&gt; addresses a file in the current directory; &lt;code&gt;/opt/example/bin/tool&lt;/code&gt; addresses an absolute location. A bare &lt;code&gt;tool&lt;/code&gt; does not automatically search the current directory. Empty or relative entries in &lt;code&gt;PATH&lt;/code&gt; can make it do so, which allows the current working directory to change the selected executable.&lt;/p&gt;
&lt;p&gt;Use explicit, trusted directories when assembling a service&amp;rsquo;s environment. A working interactive terminal does not establish that cron, a service manager, or another user has the same path or startup files. Compare their actual environments and working directories. Changing a login configuration file will not update an already running process; apply the intended environment to the process that launches the command.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;Bash normally reports &lt;code&gt;127&lt;/code&gt; when a command cannot be found and &lt;code&gt;126&lt;/code&gt; when a located command cannot be executed. These values guide investigation, but they are not a complete diagnosis: an existing script can fail because the interpreter named by its shebang is missing. Read the diagnostic and inspect the selected file&amp;rsquo;s permissions and interpreter instead of repeatedly appending directories.&lt;/p&gt;
&lt;p&gt;Do not “fix” lookup by adding writable project directories ahead of trusted tools or by using administrator privileges indiscriminately. First establish whether a function, an alias, or a cached location is responsible. &lt;code&gt;command name&lt;/code&gt; bypasses a same-named function in Bash; an explicit executable path bypasses path selection. Neither makes the selected program itself trustworthy or repairs its missing dependencies.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;This Bash example creates two disposable executables with the same name. Its temporary directories are the only additions to &lt;code&gt;PATH&lt;/code&gt;, and all changes stay inside the child shell. Cleanup names each file explicitly. No installed executable is modified.&lt;/p&gt;
&lt;p&gt;Setup uses &lt;code&gt;errexit&lt;/code&gt; to stop if fixture creation fails, then disables it before deliberately testing failures. On Bash 3.2.57, used to check this example, keeping that option enabled makes these execution-failure branches report &lt;code&gt;1&lt;/code&gt; instead of the ordinary &lt;code&gt;126&lt;/code&gt; and &lt;code&gt;127&lt;/code&gt;. Recording the shell version and options is therefore part of interpreting an unexpected status, not merely a reproducibility detail.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash --noprofile --norc &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;BASH&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;set -eu
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo_dir=$(mktemp -d)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;trap &amp;#39;rm -f &amp;#34;$demo_dir/one/demo-tool&amp;#34; &amp;#34;$demo_dir/two/demo-tool&amp;#34; &amp;#34;$demo_dir/blocked&amp;#34;; rmdir &amp;#34;$demo_dir/one&amp;#34; &amp;#34;$demo_dir/two&amp;#34; &amp;#34;$demo_dir&amp;#34;&amp;#39; EXIT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;mkdir &amp;#34;$demo_dir/one&amp;#34; &amp;#34;$demo_dir/two&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;printf &amp;#39;#!/bin/sh\nprintf &amp;#34;one\\n&amp;#34;\n&amp;#39; &amp;gt; &amp;#34;$demo_dir/one/demo-tool&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;printf &amp;#39;#!/bin/sh\nprintf &amp;#34;two\\n&amp;#34;\n&amp;#39; &amp;gt; &amp;#34;$demo_dir/two/demo-tool&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;chmod +x &amp;#34;$demo_dir/one/demo-tool&amp;#34; &amp;#34;$demo_dir/two/demo-tool&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;PATH=&amp;#34;$demo_dir/one:$demo_dir/two:$PATH&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;hash -p &amp;#34;$demo_dir/two/demo-tool&amp;#34; demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;hash -r
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo-tool() { printf &amp;#39;function\n&amp;#39;; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;command demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;unset -f demo-tool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;$demo_dir/two/demo-tool&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;: &amp;gt; &amp;#34;$demo_dir/blocked&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;chmod 600 &amp;#34;$demo_dir/blocked&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;set +e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;if &amp;#34;$demo_dir/blocked&amp;#34; 2&amp;gt;/dev/null; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; printf &amp;#39;unexpected execution\n&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; printf &amp;#39;not-executable=%s\n&amp;#39; &amp;#34;$?&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;if &amp;#34;$demo_dir/missing&amp;#34; 2&amp;gt;/dev/null; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; printf &amp;#39;unexpected file\n&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; printf &amp;#39;missing=%s\n&amp;#39; &amp;#34;$?&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;BASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first three lines should be &lt;code&gt;one&lt;/code&gt;, &lt;code&gt;two&lt;/code&gt;, &lt;code&gt;one&lt;/code&gt;: normal search, an explicitly installed cache entry, and a fresh search after clearing it. Next come &lt;code&gt;function&lt;/code&gt;, &lt;code&gt;one&lt;/code&gt;, and &lt;code&gt;two&lt;/code&gt;, distinguishing function dispatch, function bypass, and an explicit path. Finally, the nonexecutable file returns &lt;code&gt;126&lt;/code&gt; and the absent path returns &lt;code&gt;127&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This proves selection behavior with controlled files. It does not reproduce every operating system loader error or service environment. For a real incident, record the resolved command and its version in the failing environment, then verify the corrected invocation there. A successful lookup in an unrelated terminal is insufficient evidence that the original failure is repaired.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html"&gt;POSIX shell command search&lt;/a&gt;, command search rules and the not-found exit status, checked 2026-06-01.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tiswww.case.edu/php/chet/bash/bashref.html#Command-Search-and-Execution"&gt;Bash reference manual: command search and execution&lt;/a&gt;, function and builtin precedence plus cached executable paths, checked 2026-09-07.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>