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
). Script B
is in my current directory (a requirement of the program I'm using). The software's manual makes reference to bash
, however comments in A
suggest it was developed in ksh
. I've been operating in bash
so far.
Inside A
, the line to execute B
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 added pwd
, ls
, whoami
, echo $SHELL
, and echo $PATH
lines to A
to debug and confirmed that B
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 if A
contains ./B
syntax.
. A
works for A
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 using bash
.
UPDATE2: Added checking to verify $PATH
is the same.
UPDATE3: The script says it was written for ksh
, but it is running in bash
. In response to Kenster's answer, I found that running bash -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 running bash
in a POSIX-compliant mode, whereas the command line is not. Looking a little closer, I see this in the bash
man
page:
When invoked as sh, bash enters posix mode after the startup files are read.
The shebang
for A
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 the shebang
is #!/bin/sh
(instead of, e.g., #!/bin/bash
. This is the critical difference between the command line and script runtime environments that leads to A
being unable to find B
.
Let's start with how the command path works and when it's used. When you run a command like:
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 namedls
. If it finds one, it executes that file. In the case ofls
, 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:
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: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.So, your script tries to execute
. B
and fails claiming thatB
doesn't exist, even though there's a file namedB
right there in your current directory. As discussed above, the shell would have searched your command path forB
becauseB
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 sourceB
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 usingbash
. 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.