Search code examples
shzshshebang

How is it determined which shell runs a script?


There are several shells in my Linux, like bash, sh, zsh...

And I know there are at least 3 ways to specify a shell to execute my shell scripts:

  1. Specify in shebang, like#!/usr/bash
  2. Specify in the command line when executing, likesh MyScripts.sh
  3. Specify in $SHELL (say, SHELL=zsh), and execute the script like ./MyScripts.sh

So, my big question is when there is conflict between these three ways, which rule has the highest priority? Which the lowest one?

And what if I do sh MyScripts.zsh? You see, the suffix and binary don't match.


Solution

  • File names are just file names. They can be anything, except zero byte and /. Suffix is just for humans. They can be anything. They do not matter on Linux.

    When you type cmd ./filename then shell executes command cmd with argument ./filename. It does not matter if cmd is python, zsh, bash or wc, it is getting executed with the first argument set to ./filename. Now what the program does with the string ./filename is up to the program. sh is a program, that when it has one argument that does not start with a leading -, then that program sh reads that a file named as the first argument and interprets its content. So sh ./filename executes ./filename as a sh script.

    "Interpreter" is a type of programs, that are meant to interpret the content of a file in a specific programming language. For example program python is a Python programming language interpreter. sh meant to be an interpreter for POSIX-compatible Shell programming language. Etc.

    When you type ./filename in shell, then shell (or kernel) checks if the file has executable permissions set. If it does, it reads two first bytes of the file. If the two first bytes of the file are #!, then it reads to the end of the first line of the file. Then the first line is interpreted as the command to run the filename with. So when you do ./filename and you have #!anything_here then the shell runs anything_here ./filename.

    SHELL is unrelated here, it's sometimes not even set. It's usually a variable that is set to the path to the current interpreter. So if you want to run a file with the same interpreter as currently running, you would type $SHELL ./filename, so shell can expand variable SHELL and run the program the same as current interpreter you are running on.