4

From running help . or help source

Execute commands from a file in the current shell.

Read and execute commands from FILENAME in the current shell. The entries in $PATH are used to find the directory containing FILENAME.

From my point of view, it seems like the dot command (or the source command) is simply running a shell script in the current shell context (instead of spawning another shell).

Question: why doesn't . (or source) requires the file to be executable like when you run a normal script?

  • 1
    why should it? when making a question like this, it would help if you explain why you find the behavior surprising. Otherwise, the same could be asked: why aren't C source file executables? etc. –  Feb 18 '19 at 13:20
  • This behavior is surprising to me because normally running a shell script as in ./ script requires the script to be executable. – Tran Triet Feb 18 '19 at 13:47
  • @UncleBilly So that you don't accidentally type . ~/.bash_history instead of . ~/.bash_profile, for example? https://twitter.com/munificentbob/status/1091220194020622336 – Joker_vD Feb 18 '19 at 15:55

2 Answers2

6

Lets say I have a shell script (my-script.sh)starting with:

#!/bin/sh

If the script has execute permissions set then I can run the script with:

./my-script.sh

In this case you are ultimately asking the kernel to run my-script.sh as a program, and the kernel (program loader) will check permissions first, and then use /bin/sh ./my-script.sh to actually execute your script.

But the shell (/bin/sh) does not care about execute permissions and doesn't check them. So if you call this ...

/bin/sh ./my-script.sh

... The kernel is never asked to run my-script.sh as a program. The kernel (program loader) is only asked to run /bin/sh. So the execute permissions will never me checked. That is, you don't need execute permission to run a script like this.


To answer your question:

The difference between you calling ./my-script.sh and . ./my-script.sh inside another script is exactly the same. In the first, you are asking the kernel to run it as a program, in the second, you are asking your current shell to read commands from the script and the shell doesn't need (or care about) execute permissions to do this.


Further reading:

Running scripts as programs is surprising behaviour when you think about it. They are not written in machine code. I would read up on why this works; start with reading up on the shebang (#!) https://en.wikipedia.org/wiki/Shebang_(Unix)

Running scripts with the dot notation is necessary to share variables. All other mechanisms for running start a new shell "context", meaning that any variables set in the called script will not be passed back to the calling script. Bash documentation is a little lite, but it's here: https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html

  • 1
    When sourced, then $0 is your bash. If the script is executed, then $0 is ./my-script.sh. – Freddy Feb 18 '19 at 12:18
  • Thank you very much. Your answer surprises me by the fact that I could run /bin/bash non-executable-file. – Tran Triet Feb 18 '19 at 12:21
  • 1
    @Philip Couling When sourced with source my-script.sh or . my-script.sh. – Freddy Feb 18 '19 at 12:24
  • 2
    @TranTriet Also be aware that running a script with bash ./my-script.sh or . ./my-script.sh, the shebang (#!/bin/sh) will be completely ignored. This can be a problem when crossing between different shells. – Philip Couling Feb 18 '19 at 12:27
  • @Philip This is very interesting to me and is the first time I've learnt about this subtle difference. Can you recommend me good reads about other aspects of "asking the kernel to run it as a program" and "asking your current shell to run the script"? – Tran Triet Feb 18 '19 at 12:28
  • @TranTriet Any manual, tutorial or other text that covers the source or . ("dot") command should mention this. Judging from your question, you already seem to understand the difference. – Kusalananda Feb 18 '19 at 13:18
3

When you say source script.sh or . script.sh you are never executing the script. What you are running is a shell command source which does something. This "something" includes reading from script.sh and executing what-has-been-read. Your script needs to be readable for this. No need for executability.

The behaviour is similar to running bash non-executable-script.sh or python non-executable-script.py etc.

Met
  • 131