are there any other differences in functionality/behaviour?
When your file has executable permissions and is executed, then the shebang line is parsed by the kernel.
When your file is executed under the shell like bash ./script.sh then the shebang is just a comment. So it will be ignored, and your script will be run with whatever the callers flags are. Putting your flags after the shebang will make sure proper flags are set in your scripts in either case.
The shebang is parsed by kernel. That basically means that the behavior differs from kernel to kernel, from operating system to operating system. Some operating systems didn't handle arguments in shebang at all and ignored all the arguments. Some kernels parse for example #!/bin/sh -a -b as execl("/bin/sh", "-a -b") some as execl("/bin/sh", "-a", "-b"). The parsing of the shebang line to executable and arguments is done by some other code different that your shell. Sometimes if there is a space after #! like #! /bin/sh utilities don't recognize it as a valid shebang. There's even a recent linux kernel regression with too long shebang line.
The behavior of how shebang is interpreted differs between systems, so you can't be certain, so it's best to set options after the shebang.
Is behaviour same in a POSIX shell, too?
A POSIX shell doesn't (have to) interpret your shebang. If you are asking if executing sh -e and set -e has the same behavior in posix shell, then yes, the option -e on command line has the same behavior as set -e.
I couldn't find a specification of shebang line nor how should it be interpreted in posix specification. I can see in execve documentation:
Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string "#!" and using the remainder of the first line of the file as the name of the command interpreter to execute.
Those "historical implementations" seem to be very widely used still today.
The shebang line is parsed by the kernel after exec* calls. But when you are doing sh <script> or popen or system the shell can (but doesn't have to) interpret the shebang line by itself as a extension and not rely on kernel implementation, from posix:
Shell Introduction
- The shell reads its input from a file (see sh), from the
−c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-200x. If the first line of a file of shell commands starts with the characters"#!",the results are unspecified.
As for bash, it looks like bash first tries execve, then if it can't find the reason why kernel failed to run the executable, if the file has a shebang, then it parses shebang on its own to find out the interpreter.