POSIX (IEEE Std 1003.1-2017, Section 2.12) specifies:
A subshell environment shall be created as a duplicate of the shell environment, except that signal traps that are not being ignored shall be set to the default action. Changes made to the subshell environment shall not affect the shell environment.
So you are correct that a subshell would have access to everything in the calling shell, but incorrect about commands such as export
being able to modify the parent shell's environment.
And of course, the format is also specified by POSIX as follows:
( compound-list )
Execute compound-list in a subshell environment; ...
Is there an equivalent shebang that would yield the same behavior by calling ./myscript.sh instead?
Assuming you have a shebang present in your script. It will be run using whatever interpreter you specified (e.g. bash), but as a separate process, rather than as a subshell. But this would still achieve your desired result of having a script running that would not modify the parent shells environment.
$ cat script.sh
#!/usr/bin/env bash
VAR=modified
echo "VAR is $VAR"
echo "PID: $$"
$ echo "Parent: $$"
Parent: 793915
$ VAR=initial
$ ./script.sh
VAR is modified
PID: 799149
$ echo $VAR
initial
$ bash script.sh
VAR is modified
PID: 799784
$ echo $VAR
initial
$ . script.sh
VAR is modified
PID: 793915
$ echo $VAR
modified
Note: The dot .
command is equivalent to source
.
Update: Forgot to address the fact that running as a script would not export any environment variables over, unless the set -a
option is set. Though keep in mind that only variables assigned after the option is on would be exported. If this is not something you desire, then your best bet would be on (. script.sh)
From the manual:
-a
When this option is on, the export attribute shall be set for each variable to which an assignment is performed
$ cat script.sh
#!/usr/bin/env bash
echo "VAR is $VAR"
VAR=modified
echo "VAR now set to $VAR"
$ VAR=original
$ (. script.sh)
VAR is original
VAR now set to modified
$ echo $VAR
original
$ # works as expected, unchanged
$ ./script.sh
VAR is
VAR now set to modified
$ # var not sent over
$ set -a
$ VAR=original
$ ./script.sh
VAR is original
VAR now set to modified
$ echo $VAR
original
$ # works, and your variable remains unchanged
$ set +a # turn the option off