Sometimes when I'm writing a little script, I test out a bit of code by pasting into a console. And if I had a reference to $PSScriptRoot
in that code, it blows up, usually looking for files in the root of my drive. E.g. Get-Content "$PSScriptRoot\Version.txt"
turns into Get-Content "\Version.txt"
(which in turn becomes an access for "C:\Version.txt", which of course does not exist).
The current behavior of $PSScriptRoot
returning null/empty when there is no script running makes sense from the point of view that, "hey, there is no script, so... no script root". However, in practice I think it might be much more convenient for the value of $PSScriptRoot
to be the current [PowerShell] directory ($PWD
) when accessed interactively.
This certainly wouldn't solve all uses of $PSScriptRoot
from the command line (what if you're not in the right directory?), but it would certainly improve a particular common case.
Is this a breaking change? I think it's in the "unlikely grey area" bucket--it seems unlikely that somebody would take a dependency on $PSScriptRoot
being empty when accessed from the command line (though certainly not impossible, such as if someone tried to write a guard to detect being pasted into a console instead of being run from script).
It seems we can not detect interactive input.
@iSazonov : well... what if any time $PSScriptRoot
is going to return null
, then just return $PWD
instead?
Making it work interactively will make the name of this variable meaningless.
FWIW, $PSScriptRoot
is the empty string, not $null
, when used interactively. I don't know if in any possible future we would be working on a platform where the empty string is a valid absolute path.
BTW, don't join paths yourself. Use [System.IO.Path]::Combine
instead.
This is an interesting idea, it's definitely tripped me up before.
I do think it's a risky change though - there are too many scenarios where $PSScriptRoot
might not have a useful value, and I think it's likely real scripts might use that detail in some meaningful way.
Maybe what you really want is to replace $PSScriptRoot
with $PWD
when pasting? It seems simple to do as a custom PSReadLine key binding.
We've also seen people have issues with this in VSCode with the PowerShell extension. This crops up when you select script that refers to $PSScriptRoot and press F8 to run the selected script in the console. In this case, the extensions "knows" the path for the script. If we could just set the $PSScriptRoot variable in the global session, then F8 would work better for folks.
For PSRL you can override PSConsoleHostReadLine
like this
function PSConsoleHostReadLineProxy {
Microsoft.PowerShell.Core\Set-StrictMode -Off
$command = [Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($host.Runspace, $ExecutionContext)
$ast = [System.Management.Automation.Language.Parser]::ParseInput(
$command,
[ref] $null,
[ref] $null)
$findVariablesDelegate = {
param($a)
end {
$a -is [System.Management.Automation.Language.VariableExpressionAst] -and
$a.VariablePath.UserPath -eq 'PSScriptRoot'
}
}
$variables = $ast.FindAll($findVariablesDelegate, $true) |
Microsoft.PowerShell.Utility\Sort-Object { $PSItem.Extent.StartOffset } -Descending
$newPrompt = [System.Text.StringBuilder]::new($command)
foreach ($variable in $variables) {
$extent = $variable.Extent
$null = $newPrompt.
Remove($extent.StartOffset, $extent.EndOffset - $extent.StartOffset).
Insert($extent.StartOffset, '$($PWD.ProviderPath)')
}
return $newPrompt.ToString()
}
Microsoft.PowerShell.Utility\New-Alias PSConsoleHostReadLine PSConsoleHostReadLineProxy -Force
Worth noting though that this won't work in the VSCode preview extension because it doesn't actually call PSConsoleHostReadLine
. Maybe that should be revisited though.
Writing code with $PSScriptRoot
is a best practice to avoid opaque dependencies on shell location. However, this makes testing snippets very frustrating, as they must be rewritten. This feature would be immensely valuable on a daily basis.
Most helpful comment
We've also seen people have issues with this in VSCode with the PowerShell extension. This crops up when you select script that refers to $PSScriptRoot and press F8 to run the selected script in the console. In this case, the extensions "knows" the path for the script. If we could just set the $PSScriptRoot variable in the global session, then F8 would work better for folks.