File execution with dot space versus dot slash
Let's start with how the command path works and when it's used. When you run a command like:
ls /tmp
The ls
here doesn't contain a / character, so the shell searches the directories in your command path (the value of the PATH environment variable) for a file named ls
. If it finds one, it executes that file. In the case of ls
, it's usually in /bin
or /usr/bin
, and both of those directories are typically in your path.
When you issue a command with a / in the command word:
/bin/ls /tmp
The shell doesn't search the command path. It looks specifically for the file /bin/ls
and executes that.
Running ./A
is an example of running a command with a / in its name. The shell doesn't search the command path; it looks specifically for the file named ./A
and executes that. "." is shorthand for your current working directory, so ./A
refers to a file that ought to be in your current working directory. If the file exists, it's run like any other command. For example:
cd /bin
./ls
would work to run /bin/ls
.
Running . A
is an example of sourcing a file. The file being sourced must be a text file containing shell commands. It is executed by the current shell, without starting a new process. The file to be sourced is found in the same way that commands are found. If the name of the file contains a /, then the shell reads the specific file that you named. If the name of the file doesn't contain a /, then the shell looks for it in the command path.
. A # Looks for A using the command path, so might source /bin/A for example
. ./A # Specifically sources ./A
So, your script tries to execute . B
and fails claiming that B
doesn't exist, even though there's a file named B
right there in your current directory. As discussed above, the shell would have searched your command path for B
because B
didn't contain any / characters. When searching for a command, the shell doesn't automatically search the current directory. It only searches the current directory if that directory is part of the command path.
In short, . B
is probably failing because you don't have "." (current directory) in your command path, and the script which is trying to source B
is assuming that "." is part of your path. In my opinion, this is a bug in the script. Lots of people run without "." in their path, and the script shouldn't depend on that.
Edit:
You say the script uses ksh
, while you are using bash
. Ksh follows the POSIX standard--actually, KSH was the basis for the POSIX standard--and always searches the command path as I described. Bash has a flag called "POSIX mode" which controls how strictly it follows the POSIX standard. When not in POSIX mode--which is how people generally use it--bash will check the current directory for the file to be sourced if it doesn't find the file in the command path.
If you were to run bash -posix
and run . B
within that bash instance, you should find that it won't work.
Related videos on Youtube
TTT
I'm a scientist/engineer (not computer/software-related) that does a fair amount of coding, including numerical modeling and various kinds of data analytics. My formal training in coding is quite limited, a 1-semester course in Matlab (a high-level science/engineering language) and another semester course in real-time systems coding in C/C++. Though because I do it fairly regularly (1-10 hours/week) and because it interests me, I've worked hard to gain a deeper understanding of the both the details and the "big picture" on the software and hardware sides of systems I use.
Updated on June 22, 2022Comments
-
TTT almost 2 years
I am attempting to work with an existing library of code but have encountered an issue. In short, I execute a shell script (let's call this one
A
) whose first act is to call another script (B
). ScriptB
is in my current directory (a requirement of the program I'm using). The software's manual makes reference tobash
, however comments inA
suggest it was developed inksh
. I've been operating inbash
so far.Inside
A
, the line to executeB
is simply:. B
It uses the "dot space" syntax to call the program. It doesn't do anything unusual like
sudo
.When I call
A
without dot space syntax, i.e.:./A
it always errors saying it cannot find the file
B
. I addedpwd
,ls
,whoami
,echo $SHELL
, andecho $PATH
lines toA
to debug and confirmed thatB
is in fact right there, the script is running with the same$SHELL
as I am at the command prompt, the script is the same user as I am, and the script has the same search path$PATH
as I do. I also verified if I do:. B
at the command line, it works just fine. But, if I change the syntax inside
A
to:./B
instead, then
A
executes successfully.Similarly, if I execute
A
with dot space syntax, then both. B
and./B
work.Summarizing:
./A
only works ifA
contains./B
syntax.
. A
works forA
with either./B
or. B
syntax.I understand that using dot space (i.e.
. A
) syntax executes without forking to a subshell, but I don't see how this could result in the behavior I'm observing given that the file is clearly right there. Is there something I'm missing about the nuances of syntax or parent/child process workspaces? Magic?UPDATE1: Added info indicating that the script may have been developed in
ksh
, while I'm usingbash
.
UPDATE2: Added checking to verify$PATH
is the same.UPDATE3: The script says it was written for
ksh
, but it is running inbash
. In response to Kenster's answer, I found that runningbash -posix
then. B
fails at the command line. That indicates that the difference in environments between the command line and the script is that the latter is runningbash
in a POSIX-compliant mode, whereas the command line is not. Looking a little closer, I see this in thebash
man
page:When invoked as sh, bash enters posix mode after the startup files are read.
The
shebang
forA
is indeed#!/bin/sh
.In summary, when I run
A
without dot space syntax, it's forking to its own subshell, which is in POSIX-compliant mode because theshebang
is#!/bin/sh
(instead of, e.g.,#!/bin/bash
. This is the critical difference between the command line and script runtime environments that leads toA
being unable to findB
.-
Reinstate Monica Please almost 10 yearsIs
B
executable?. B
doesn't technicallyexecute
B
, itsources
it. If one of them isksh
, try usingksh yourscript
-
TTT almost 10 yearsYes,
B
is executable. I'm not clear on the difference between the two, will read now. -
TTT almost 10 yearsNever mind, I see
source
is the term (and a command) for running something in the same shell.
-
-
TTT almost 10 yearsYou're right about the current directory not being in
$PATH
. I had thought about this, but then why does. B
work at the command line? I triedecho $PATH
and it's the same in both the script and my. That's the real core of my question, why does the script fail where the command line succeeds if it looks like everything (shell, environmental variables, etc.) is the same? -
TTT almost 10 yearsYep, fails with
bash -posix
. I went back and figured out why the script was running with POSIX-compliant mode and added a full explanation to the original question (wouldn't fit here). -
tripleee about 6 yearsMaybe also explain that sourcing a file in the current process means the sourced file can manipulate your environment. So if you run
. B
andB
doesfoo=bar
then this variable will be set whenB
finishes, until the current script or interactive shell exits, whereas./A
cannot change your variables (a subprocess cannot change its parent).