Home > Net >  How to start another PowerShell session in a separate window while keeping the environment?
How to start another PowerShell session in a separate window while keeping the environment?

Time:03-09

While working in PowerShell I tend to quickly switch to admin mode by typing

Start-Process wt -Verb runas

When I do so, a new window appears (sadly, no sudo in Windows). In that new session however, the environment is totally fresh. Is it possible to keep variables, aliases, working dir and all other stuff of similar matter while jumping to a new window? If not, then well, it's a valid answer.


To give some example, I am looking for this behavior:

First window

C:\test> $x = 123
C:\test> Start-Process wt

New window

C:\test> $x
123

CodePudding user response:

By (security-minded) design, elevated sessions (-Verb RunAs) do not inherit the caller's environment variables.

Also, wether or not you use -Verb RunAs, the state of a PowerShell session (aliases, functions, current location, ...) is never inherited when you launch another PowerShell process, such as with Start-Process.


You can work around the issue by explicitly and selectively redefining the state of interest via commands executed in the elevated session, based on values from the caller's state, but that is quite cumbersome and has limitations, as the following example shows:

# Define a few things to copy to the elevated session.
$x1 = 123
$x2 = '3" of snow' # !! See the caveat re regular variables below.
$env:foo = 1
$env:foo2 = 2
Set-Alias bar Get-Date
function baz { "hello, world" }

# Note: The following only copies the definitions above.
#       You could try to copy ALL such definitions, by omitting a target name / pattern:
#         Get-ChildItem env:
#         Get-ChildItem function:
#         Get-ChildItem alias:
#       CAVEAT: This will NOT generally work with *regular variables*.
Start-Process -Verb RunAs powershell @"
-NoExit -Command Set-Location -LiteralPath \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
$(Get-Variable x? | ForEach-Object { "`${$($_.Name)} = $(if ($_.Value -is [string]) { "'{0}'" -f ($_.Value -replace "'", "''" -replace '"', '\"')  } else { $_.Value }); " })
$(Get-ChildItem env:foo* | ForEach-Object { "Set-Item \`"env:$($_.Name)\`" \`"$($_.Value -replace '"', '\"\"')\`"; " })
$(Get-ChildItem function:bar | ForEach-Object { "`$function:$($_.Name) = \`"$($_.Definition -replace '"', '\"\"')\`"; " })
$(Get-ChildItem alias:baz | ForEach-Object { "`$alias:$($_.Name) = \`"$($_.Definition)\`"; " })
"@

Important:

  • I've omitted the call to Windows Terminal (wt.exe), as that would create another PowerShell session, which means that only the following definitions would be preserved for that session:

    • Environment variables.
    • The current location (working directory), IF its default shell is configured to use the parent process' working directory. Alternatively, and more predictably, pass the working dir. explicitly with the -d option:
      wt.exe -d \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
    • If that is enough, you can remove the commands that preserve aliases, functions, and regular variables, add -WindowStyle Hidden to Start-Process, remove -NoExit before -Command in the argument list, and add a wt.exe call at the bottom.
  • Preserving the other types of definitions, requires working directly in the elevated powershell session, which will use a regular (conhost.exe) console window, however.

In general, it's best to place definitions that should be available in both regular and elevated sessions in your $PROFILE file.

Complementarily, see this answer for convenience function Enter-AdminPSSession, which allows you to pass a script block to execute in the elevated session, to which you can pass values from the caller's state as arguments.

Note:

  • The above uses the Windows PowerShell CLI, powershell.exe. To use PowerShell (Core) 7 instead, substitute pwsh.exe.

  • The above covers preserving the current file-system location, environment variables, aliases, and functions in a generic fashion.

  • Caveat: By contrast, preserving regular variables is limited to strings and numbers - in essence, instances of those data types whose stringified representation is recognized as such when interpreted as a source-code literal.

    • With nontrivial additional effort, supporting more data types is possible, by using Base64 encoding with the CLI's -EncodedCommand and -EncodedArguments parameters as shown in this answer, but the range of types that can be represented with type fidelity is fundamentally limited by PowerShell's XML-based serialization infrastructure - see this answer.

CodePudding user response:

You can not keep variables, you will lose them immediately after the new window is created, the best you can do instead is to create a script containing all your activities then save it in the same working directory.

When you open a new window just call your script that will be able to give you the same information as in the other window.

  • Related