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 sort of per-process basis, enabling the creation of child processes with distinct characteristics. This is particularly useful when you want to modify environment variables within a confined scope.
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 (back to original)
echo "PATH after subshell: $PATH"
echo " "
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 "This path only affects the current subshell":
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
Printing out to the console before, inside, and after the subshell. Only inside does our command run and have access to the particular path we've provided, which points to our do_stuff.sh
script.
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 %PATH%
like so, with cmd.exe /c echo %PATH%
:
Now let's use the System.Diagnostics capabilities provided by .NET's ProcessStartInfo attribute to create a New-Object
called $x
, 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, disable UseShellExecute
so our new object doesn't use the shell's default variables and will start the process directly from our executable, and finally create a new object $p
, which we'll hook to System.Diagnostics.Process
, then set the StartInfo
for our new $p
object to $x
, and launch it. 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. We've created a new subshell with custom environment variables, removed its regular system Path
, and reconfigured it's %PATH%
to our custom directory. And after our program runs, we can confirm we didn't affect any of the global variables in our main shell.
No comments:
Post a Comment