title: Bash script execution with and without shebang in Linux and BSD
body: <p>How and who determines what executes when a Bash-like script is executed as a binary without a shebang?</p>
  
  <p>I guess that running a normal script <em>with</em> shebang is handled with <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c?id=HEAD" rel="noreferrer">binfmt_script</a> Linux module, which checks a shebang, parses command line and runs designated script interpreter.</p>
  
  <p>But what happens when someone runs a script without a shebang? I've tested the direct <code>execv</code> approach and found out that there's no kernel magic in there - i.e. a file like that:</p>
  
  <pre><code>$ cat target-script
  echo Hello
  echo "bash: $BASH_VERSION"
  echo "zsh: $ZSH_VERSION"
  </code></pre>
  
  <p>Running compiled C program that does just an <code>execv</code> call yields:</p>
  
  <pre>
  $ cat test-runner.c
  void main() {
          if (execv("./target-script", 0) == -1)
                  perror();
  }
  $ ./test-runner
  ./target-script: Exec format error
  </pre>
  
  <p>However, if I do the same thing from another shell script, it runs the target script using the same shell interpreter as the original one:</p>
  
  <pre>
  $ cat test-runner.bash
  #!/bin/bash
  ./target-script
  
  $ ./test-runner.bash 
  Hello
  bash: 4.1.0(1)-release
  zsh: 
  </pre>
  
  <p>If I do the same trick with other shells (for example, Debian's default <code>sh</code> - <code>/bin/dash</code>), it also works:</p>
  
  <pre>
  $ cat test-runner.dash   
  #!/bin/dash
  ./target-script
  
  $ ./test-runner.dash 
  Hello
  bash: 
  zsh: 
  </pre>
  
  <p>Mysteriously, it doesn't quite work as expected with zsh and doesn't follow the general scheme. Looks like zsh executed <code>/bin/sh</code> on such files after all:</p>
  
  <pre>
  greycat@burrow-debian ~/z/test-runner $ cat test-runner.zsh 
  #!/bin/zsh
  echo ZSH_VERSION=$ZSH_VERSION
  ./target-script
  
  greycat@burrow-debian ~/z/test-runner $ ./test-runner.zsh 
  ZSH_VERSION=4.3.10
  Hello
  bash: 
  zsh: 
  </pre>
  
  <p>Note that <code>ZSH_VERSION</code> in parent script worked, while <code>ZSH_VERSION</code> in child didn't!</p>
  
  <p>How does a shell (Bash, dash) determines what gets executed when there's no shebang? I've tried to dig up that place in Bash/dash sources, but, alas, looks like I'm kind of lost in there. Can anyone shed some light on the magic that determines whether the target file without shebang should be executed as script or as a binary in Bash/dash? Or may be there <em>is</em> some sort of interaction with kernel / libc and then I'd welcome explanations on how does it work in Linux and FreeBSD kernels / libcs?</p>