Or rather, subshells in Bash and Powershell. A subshell functions as a sort of isolated environment for executing commands, creating a subprocess or child process within the parent shell. It lets a user define specific environment variables on a per-process basis, enabling the creation of child processes with distinct characteristics.
In Bash
Imagine you have a Bash script that could alter certain exports, but you don't want these changes to affect the global system values. Enter subshells. Here's a simple example. Subshells in Bash are broken into and out of using parentheses:
#!/bin/bash
echo "PATH before subshell: $PATH"
echo " "
(
subshell_path="/Users/hexagr/subshell"
export PATH="$subshell_path"
echo "PATH within subshell: $PATH"
echo " "
# Execute the command using the full path
subshell_cmd="do_stuff.sh"
$subshell_cmd
echo " "
)
# Print the PATH after the subshell
echo "PATH after subshell: $PATH"
echo " "
# Try the subshell command script in the regular shell,
# but it fails because we don't have the path!
echo "Executing do_stuff.sh after subshell:"
$subshell_cmd
do_stuff.sh
The bash script's output, in combination with our other shell script do_stuff.sh
which just echo's a simple message. Access to the custom path only happens in the subshell:
$ ./test.sh
PATH before subshell: /usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin
PATH within subshell: /Users/hexagr/subshell
This path only affects the current subshell.
PATH after subshell: /usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin
Executing do_stuff.sh after subshell:
./test.sh: line 22: do_stuff.sh: command not found
A Windows Analog in Powershell
While Microsoft Windows doesn't officially specify this as a "subshell" as far as I can tell, the following strategy provides functionally similar behavior for Windows operating systems. We can utilize the .NET API to manipulate the environment variables on a per-process basis in Powershell. But before we do so, let's print our regular system shell %PATH%
like so, with cmd.exe /c echo %PATH%
:
Now let's use the System.Diagnostics capabilities provided by .NET's ProcessStartInfo class to create a New-Object
called $x
.
We'll set the filename to cmd.exe
along with our argument. Then we'll remove the original system Path
and replace it with our own custom path. We shall also disable UseShellExecute
so our new object doesn't use the shell's default variables and will instead start the process directly from our process.
Finally, we'll assign $p
to a System.Diagnostics.Process
object. And then set the StartInfo
for our new $p
object to our $x
ProcessStartInfo
object. Then launch it with $p.Start().
Thank you Microsoft Documentation and StackOverflow ;)
$x = New-Object System.Diagnostics.ProcessStartInfo
$x.FileName = "cmd.exe"
$x.Arguments = "/c echo %PATH%"
$x.EnvironmentVariables.Remove("Path")
$x.EnvironmentVariables.Add("PATH", "C:\custom\path")
$x.UseShellExecute = $false
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $x
$p.Start()
PS C:\Users\User> $p.Start() True PS C:\Users\User> C:\custom\path
We can see our new subprocess is effectively confined to the C:\custom\path
now since we created a new subshell with custom environment variables, removed its regular system Path
, and set it's %PATH%
to be our custom directory. And after our cmd.exe subprocess runs and we're back in the regular shell, we can print the default system path to confirm we didn't affect any of the global environment variables in the main shell.
No comments:
Post a Comment