vscode-powershell: `$PSScriptRoot` is not populated when running a code block (via F8)
System Details
- Operating system name and version: Windows 10
- VS Code version: 1.10.2
- PowerShell extension version:
- Output from
$PSVersionTable:
PS C:\Source\neo4j-quick-demo> $pseditor.EditorServicesVersion
Major Minor Build Revision
----- ----- ----- --------
0 11 0 0
PS C:\Source\neo4j-quick-demo>
PS C:\Source\neo4j-quick-demo> code --list-extensions --show-versions
PS C:\Source\neo4j-quick-demo>
PS C:\Source\neo4j-quick-demo> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.14393.953
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14393.953
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Issue Description
$PSScriptRoot is not populated when running a code block (via F8)
I am trying load DLLs that are in the same directory as the PowerShell script and use $PSScriptRoot to get the location for them.
When running the code in PowerShell it’s fine, but using VSCode, when running the code snippet via F8, the variable is not populated.
Repro
- Open VSCode and create a PowerShell (.ps1) file
- Add the following command
Write-Host "$PSScriptRoot\abc" - Select the newly created line and Press F8 to execute the PowerShell in the integrated terminal
Expected result:
<full working path>\abc
e.g. If the script I was editing was in C:\Source I would expect the result to be C:\Source\abc
Actual result:
\abc
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 9
- Comments: 78 (13 by maintainers)
Sure that’s a fine working practice but there is many a time when you’ve got a script you’ve written that doesn’t need testing and doesn’t need splitting into loads of little chunks that you just want to run a little bit of it. Not being able to use psscriptroot is just annoying.
Simon Sabin
From: Justin Grote @.> Sent: Friday, May 21, 2021 12:53:37 AM To: PowerShell/vscode-powershell @.> Cc: Simon Sabin @.>; Mention @.> Subject: Re: [PowerShell/vscode-powershell]
$PSScriptRootis not populated when running a code block (via F8) (#633)Or do your testing by writing it into a pester test and using the run test or debug test codelens rather than f5/f8
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/PowerShell/vscode-powershell/issues/633#issuecomment-845556082, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAJHM22UYXHISLVXTGHFSELTOWOIDANCNFSM4DFXJ4TQ.
Yes, it is still an issue for
F8. I’m not aware that this is an issue when debugging. In fact, I can’t repro this usingF5.@TylerLeonhardt This should probably be retagged as an enhancement, since it’s technically working as expected.
The ISE-Compatibility tag should also be removed as ISE does the same thing with F8 (unless I’m missing something) so it’s not a user experience compatibility thing.
I think this must be be related:
I’d be happy if it wasn’t supported, but somehow I was warned that “Stuff may not do what you expect” if it saw those tokens in the text.
One could argue that since
F8simply runs the selected code inglobalscope, that it is expected that$PSScriptRootwould not be defined. This is howF8works in ISE i.e.$PSScriptRootis not defined.Thanks for reminding me! I need to get that fixed.
I suppose what bothers me about $PSScriptRoot and $PSCommandPath is that they are not idempotent. They are populated if they are 1) part of a script and 2) run as a script. Even if it’s not run as a script, the script still has a parent folder. It seems misleading to report it as not having a parent folder. Automatic variables that depend upon context this way are going to introduce complexity. I understand that it’s complex to simplify the automatic variable. However, such complexity is done once and maintained by one team.
Here’s one of my current work arounds. Not sure how many more I’ll have. Other developers will have their own variations. I think it would be nice to have just one version that just worked without thinking about it. I wonder how many people think about this.
I was wondering if this could be solved by the debug capability rather than built in F8. i.e. being able to specify something in launch.json I however couldn’t find a way to have a script run, the script property of launch.json only seems to take a file.
By Design, but not always as expected. There are enough people that fall fowl of this and thus it would be good to have a solution. I understand the rationale, but when you do F8 and you are in a script the expectation is that
$psscriptrootis that script. The fact the implementation of F8 doesn’t run that script, is a detail of implementation/powershell that the whole script isn’t being run.@RandyInMarin there isn’t a great option for relative paths that works everywhere.
$PSScriptRootis recommended if the code will always be run as a script and you are explicitly referencing files relative to the script, because the paths will be relative to the script. Using./relative/pathis not recommended if you explicitly want something relative to where the script is located because it will be relative to the working directory, so if someone starts the script in a different path, it will break.You may want to consider consolidating your functions into a Powershell module, that way you just import the module and don’t have to reference a lot of paths relative to the script.
I’m going to close this as “working as expected”
@RandyInMarin for Powershell at least, there are several variables like $PSScriptRoot that are only present in the context of a script, not within an interactive session.
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.2#myinvocation
When you do F8, you are basically “cut pasting” into the terminal, so the current behavior is exactly what happens outside of vscode at a normal terminal.
Like I said, an opt-in setting to populate these to ease development would make sense, if someone wants to attempt a PR I’m sure we could massage it to get accepted, but it definitely wouldn’t be the default.
Or do your testing by writing it into a pester test and using the run test or debug test codelens rather than f5/f8
@SeeminglyScience It did:
https://github.com/PowerShell/PowerShellEditorServices/blob/765c76c930ffcc7f4727a2d1f1dd7ee3ac71e4ee/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs#L105-L115
It’s possible actually, @TylerLeonhardt and I talked about this awhile ago iirc.
Line breakpoints are based on the file path associated with an AST (e.g.
$ast.Extent.File) which can be fudged a bit if you create the scriptblock to be invoked viaParser.ParseInput(string input, string fileName, ...).GetScriptBlock(). The dirty part is having to arbitrarily pad the script extent so that the offsets/line/column all match. This is the same way my example of F8 PSScriptRoot works.It should even be technically possible for untitled files in the latest PowerShell. That’s assuming the change went through that allows a
Breakpointobject to be created with an arbitrary file string (e.g. an non-existing or generally invalid path). @TylerLeonhardt you know if that change went through?I’d say that 2 would be confusing. the text is not part of a script and thus psscriptroot should be empty. If you want psscriptroot to be populated save the file.
I really can’t see where you would be using psscriptroot in an file that isn’t saved to disk.
@simonsabin Maybe? My original issue was with “application code” trying to load vendored DLLs. If the code has no concept of where it’s running from then loading dependencies becomes very difficult.
@JustinGrote While this may be a workaround, it’s not really feasible to do that for every time you run F8
I’m happy with that. It’s somewhat trivial to create (I think) a runspace with no script file e.g.
test.ps1Very clever solution! If we tried that, one thing we’d want to do is send the EditorContext along with the run selection request so that it doesn’t have to be fetched from within PowerShell, saving another round trip. If the editor sends the EditorContext with the request we can take this approach, otherwise go with the original approach.
Fixing this is a little bit more difficult than it may appear. The big problem with this one is that it’s basically impossible to set the
PSScriptRootvariable manually because it’s replaced by the engine in every single scope.That said, the engine creates the variable based on the
ScriptExtentfor the command. With theParserAPI, you can parse specific input and specify a file source. Here’s a proof of concept editor command preservesMyInvocation,PSScriptExtent, and position info for breakpoints.Yeah, F8 could just insert the script’s parent dir into the session as $PSScriptRoot right before running the snippet. I believe people used to complain that the ISE did not do this. Might be nice to make it work.
However, I’ve been considering using VS Code’s built in Run Selection in Terminal command instead of my own custom F8 implementation. This would mean that I wouldn’t be able to do the $PSScriptRoot injection. However, I don’t have a good reason to do that other than just removing “unnecessary” code. If the $PSScriptRoot injection is important enough (which it might be for interactive dev workflow) then I can still keep the current F8.
Thoughts?