I am used to running shell scripts with for example
source script.sh
I was surprised to learn recently that this also works
. script.sh
The single dot of course normally indicates the current directory so I am now confused. Can anyone explain this use of .?
Taken from IBM docs: ".(dot) runs a shell script in the current environment and then returns. Normally, the shell runs a command file in a child shell so that changes to the environment by such commands as cd, set, and trap are local to the command file. The . (dot) command circumvents this feature.
If there are slashes in the file name, . (dot) looks for the named file. If there are no slashes . (dot) searches for file in the directories specified in the PATH variable. This may surprise some people when they use dot to run a file in the working directory, but their search rules are not set up to look at the working directory. As a result, the shell does not find the shell file."
. (dot) and source mean the same thing in the bash language.
In fact the (dot) form is the standard form according to the POSIX Shell Specification. The source form is a bash extension, but not a bash invention. (The venerable Berkley C-shell (csh) used to use source rather than ..)
According to the POSIX Shell specification, source is a command name whose meaning is unspecified; see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
Related
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:
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.
There are two options to run a shell script:
$ ./some/script.sh
$ . ./some/script.sh
As far as I unterstand, the first one starts a new shell environment based on the given shebang line withing the script. While the second one executes the statements within the same shell environemnt.
Are there more differences ?
Where can I find more documentation about the second one ?
Is . a real command ? I can not find a manpage to it.
Are there more differences ?
The gist of the matter is that using . the script is executed line by line in the same process. Otherwise a new process is forked. And a separate process has no way of changing the parent, for example it can't change environment variables such as the current directory.
Where can I find more documentation about the second one ?
[cnicutar#fresh ~]$ help source
source: source filename [arguments]
...
Is . a real command
[cnicutar#fresh ~]$ type .
. is a shell builtin
In case it isn't obvious already, . and source are identical*.
As rush commented, source isn't specified by POSIX so you should probably use . in code intended to be portable. The dot is specified in chapter 2.
. is a Bourne Shell command for reading a file and executing the commands in the file, your analysis is essentially correct. bash and other shells add source as an alias for ..
See the manual for bash builtins, and see . at the top of the manual for Bourne sh builtins.
Read and execute commands from the filename argument in the current shell context. If filename does not contain a slash, the PATH variable is used to find filename. When Bash is not in posix mode, the current directory is searched if filename is not found in $PATH. If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged. The return status is the exit status of the last command executed, or zero if no commands are executed. If filename is not found, or cannot be read, the return status is non-zero. This builtin is equivalent to source.
it is used to source an environment. such as the .profile.
I tried the following two ways to source the property file
#!/bin/sh
. import.properties
echo $USER_ID
echo $INPUT_FILE
It says :
./test.sh[3]: import.properties: not found
when tried using source import.properties it gave the message as:
./test.sh[3]: source: not found.
I am very new to the scripting and env. Please let me know what I am missing here?
To be found by the dot . command, the file must be readable (not necessarily executable) and on your PATH (and to be safely usable, it must contain shell script).
If the file is in your current directory and . (the directory, not the command) is not on your PATH, you can use:
. ./import.properties
Otherwise, you need to specify the absolute name of the file, or relative name of the file, or move the file to a convenient directory on your PATH.
The alternative notation, source import.properties fails because you are not in the C Shell, and because you are not using Bash. The source command in the C Shell is the analogue of the dot command in the Bourne shell. Bash allows it as a synonym for the dot command (or the dot command as a synonym for source). Since source was not found, we can safely assume your shell does not support it as a built-in.
I need to set the environment variables, usually we do this by
source script.sh
But now, I am automating it during the boot process and it looks like the root boots by default with sh shell. How do I source this script in sh?
The dot command '.' is the equivalent of the C Shell (and Bash) source command. It is specified by POSIX (see dot), and supported by the Bourne and Korn shells (and zsh, I believe).
. somefile
Note that the shell looks for the file using $PATH, but the file only has to be readable, not executable.
As noted in the comments below, you can of course specify a relative or absolute pathname for the file — any name containing a slash will not be searched for using $PATH. So:
. /some/where/somefile
. some/where/somefile
. ./somefile
could all be used to find somefile if it existed in the three different specified locations (if you could replace . with ls -l and see a file listed).
Pedants of the world unite! Yes, if the current directory is the root directory, then /some/where/somefile and ./some/where/somefile would refer to the same file — with the same real path — even without links, symbolic or hard, playing a role (and so would ../../some/where/somefile).
tl;dr With sh (as opposed to bash) the argument must contain a slash: source ./script.sh, not just source script.sh. Unless script.sh can be found in PATH.
Dot (.) command exists in both bash and sh. Additionally, bash aliases it as source. From bash manual:
https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-source
source
source filename
A synonym for . (see Bourne Shell Builtins).
https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-_002e
. (a period)
. filename [arguments]
Read and execute commands from the filename argument in the current shell context. If filename does not contain a slash, the PATH variable is used to find filename. When Bash is not in POSIX mode, the current directory is searched if filename is not found in $PATH.
From POSIX:
If file does not contain a /, the shell shall use the search path specified by PATH to find the directory containing file. Unlike normal command search, however, the file searched for by the dot utility need not be executable.
For example, when I say . .bashrc on my Linux command prompt, is there a corresponding binary/script that gets executed in place of the first dot? If the dot itself is a command, where is its location?
The . operator is shorthand for the source Bash builtin (as pointed out by John Kugelman below). Typing
help .
or
help source
at the Bash prompt will give you some information. For more on how source works, see http://www.ss64.com/bash/period.html.
Additionally I want to point out that you don't "execute" anything with it (in terms of fork/exec), which is very important (and probably the only reason '.' exists).