Powershell via VBScript - Environmental Variable path terminated when username contains spaces [duplicate] - powershell

This question already has answers here:
How can I run a PowerShell script with white spaces in the path from the command line?
(5 answers)
Closed 1 year ago.
I have been working on a Powershell Winforms app that requires the console window to be hidden. To do this, I am calling a .ps1 script from a .vbs file (as starting via another .ps1 script and using "-WindowStyle Hidden" still briefly shows the console window upon opening the script). I am using the following code:
Dim shell,command
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1"""
Set shell = CreateObject("WScript.Shell")
shell.Run command,0
This works with no issues when using an account name with no spaces (e.g. "TESTUSER" will resolve to "C:\USERS\TESTUSER\AppData\Local"). However, when any part of the path generated by the "%localappdata%" environmental variable contains spaces (in this case, using something like "TEST USER 1"), Powershell will terminate the command at "C:\Users\TEST" with an error stating: "The term "C:\Users\TEST" is not recognised as the name of a cmdlet, function, script file or operable program."
I am aware that any strings with spaces in VBScript need to use two sets of surrounding double quotes (""path with spaces""), but this doesn't work in this case - I have tried every combination that has been suggested and the %localappdata% path still has the same issue.
Things I have tried:
Two quotes surrounding path (Working with no spaces):
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1"""
Two quotes surrounding entire argument:
command = ""powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1""""
Single quotes surrounding both:
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command "%localappdata%\test\test.ps1""
Expanding %localappdata% variable:
Dim shell,path,command
Set shell.CreateObject("WScript.Shell")
path = shell.ExpandEnvironmentStrings("%localappdata%")
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command " & path &"\test\test.ps1"
shell.Run command,0
Adding "Chr(34)" to replace spaces:
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1""" & Chr(34)
Using:
"$env:localappdata" or "$env:username"
(both inside and out of "command" quotes) to replace
"%localappdata%" or "C:\Users\%username%\AppData\Local"
I have also tried various solutions provided here and here, but nothing works in this case.
I also tried replacing %localappdata% with the absolute file path ("C:\Users\TEST USER 1\AppData\Local\test\test.ps1"), but this also gives the same error.
Any help would be greatly appreciated! Thanks in advance.

Since you're invoking a script file by path rather than passing PowerShell statements to PowerShell's CLI, use the -File parameter, not -Command, which implicitly solves your quoting problems:
' Note the use of -File instead of -Command
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File ""%localappdata%\test\test.ps1"""
The reason that -Command didn't work in your case is that its argument(s) are subject to another round of interpretation, namely as PowerShell code, after stripping syntactic " chars. during command-line parsing. This means that a path with spaces is then seen unquoted by PowerShell, causing the invocation to fail; you'd need additional quoting -either escaped " quotes (\"...\", i.e. \""...\"" from inside a VBScript string) or single quotes ('...'), which in turn would necessitate use of &, the call operator:
' With -Command: note the embedded '...' quoting and the need to call with `&`
' However, there's usually no need for -Command to invoke scripts with arguments.
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command ""& '%localappdata%\test\test.ps1'"""
See this answer for more information.

Related

Cannot access $Profile.CurrentUserAllHosts using pwsh.exe?

I want to read the automatic variable $profile.CurrentUserAllHosts, using a Deno script.
I already know how to execute pwsh.exe from Deno.
But would like to know how to invoke pwsh.exe to get the $profile.CurrentUserAllHosts value.
For example, I expect that two commands below yields the same results rather than different results:
PS> $profile.CurrentUserAllHosts
C:\Users\Flavio\Documents\PowerShell\profile.ps1
PS> pwsh.exe -noprofile -Command "{$profile.CurrentUserAllHosts}"
C:\Users\Flavio\Documents\PowerShell\Microsoft.PowerShell_profile.ps1.CurrentUserAllHosts
Note: I'm using Powershell 7.2.5, on Windows 10 and I can guarantee that pwsh.exe is the same version for all instances.
There's two things to keep in mind here:
You're running the command in PowerShell itself, so $profile is actually expanded in your parent-session due to the double-quotes in your -Command argument.
You're passing a string with a scriptblock as the -Command argument. It's not actually executed - you're just getting the result of {$profile.CurrentUserAllHosts}.ToString() which is the content of the scriptblock.
Suggestions:
Add a call operator to invoke the scriptblock in the string, ex -Command '& { $profile.CurrentUserAllHosts }'. If invoking pwsh from PowerShell you can also use pwsh.exe -noprofile -Command { $profile.CurrentUserAllHosts }.
Or provide the command as a simple string, ex. -Command '$profile.CurrentUserAllHosts'
In both alternatives, remember single quotes OR escaping the dollar-sign with backtick.

How to pass a string array to powershell through vbscript?

I'm trying to pass a string array to powershell through vbscript.
I got the params in the file.ps1 script:
param (
[string[]]$target
)
I'm trying to call file.ps1 through vbscript.
I want $target = #('c:\dev','d:\lib') to be passed in.
I currently have in my vbscript :-
target1 = """c:\dev"", ""d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -noexit -ExecutionPolicy Unrestricted -file .\file.ps1 -target1 """ & target & """")
which returns:
c:\dev, d:\lib and which isn't the powershell string array format "c:\dev", "d:\lib"
Update - Thanks mklement0 and alex-dl for your answers!
alex-dl's helpful answer contains an effective solution, as long as the array's elements don't need quoting, but let me try to break it down conceptually in more detail:
The -File parameter of PowerShell's CLI, powershell.exe (pwsh in PowerShell (Core) 7+) fundamentally does not support passing arrays to PowerShell code, because all arguments are interpreted strictly as whitespace-separated, with verbatim content.
E.g., "c:\dev", "d:\lib" on the command line is parsed as two arguments:
c:\dev, (note the trailing ,, syntactic " quotes stripped)
d:\lib
You must use the -Command (-c) option in order to pass arrays.
-Command fundamentally changes how the arguments are parsed:
All arguments are stripped of syntactic (non \"-escaped) " quotes.
The resulting tokens are space-concatenated to form a single string.
The resulting string is then interpreted as PowerShell code, i.e. as if you had submitted it from inside a PowerShell session.
This therefore enables all of PowerShell's features, such as passing arrays with ,, '...' (single-quoting), ..., and notably also means that references to PowerShell variables (e.g. $HOME) are recognized (unlike with -File).
See this answer for more guidance on when to use -File vs. -Command (-c).
Therefore, the best approach in your case is:
target = "'c:\dev', 'd:\lib'" ' Note the embedded '...'-quoting
Set objShell = CreateObject("Wscript.Shell")
' Note the use of -c (-command) instead of -file
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
Using embedded '...'-quoting simplifies passing the array elements in a way that PowerShell sees them as individually quoted (with the values at hand, this isn't strictly necessary, but it may be in other cases).
Doing this with embedded "..."-quoting gets unwieldy, because each embedded " must then be escaped as \"" (sic):
"" to escape the " inside the VBScript string,
and \ to ensure that \" is ultimately passed on the command line, which PowerShell requires for escaping " on the command line[1] (whereas PowerShell-internally, it is `" or (alternatively, inside a double-quoted string) "").
target = "\""c:\dev\"", \""d:\lib\"""
Set objShell = CreateObject("Wscript.Shell")
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
[1] While in Windows PowerShell you can situationally get away with "" instead of \", it can break, especially if the overall -c argument is enclosed in "...".
This problem has been fixed in PowerShell (Core) 7+, where you can now use "" and \" interchangeably.
E.g., when invoked from cmd.exe,
powershell -c " ""ab c"".length " breaks, whereas
pwsh -c " ""ab c"".length " works.
You could use the following vbscript:
target1 = """c:\dev, d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -Command .\file.ps1 -target " & target1)
And this powershell script:
param (
[string[]]$target
)
foreach ($t in $target) {
write-output $t
}
Read-Host -Prompt "Press Enter to exit"
Without -Command the argument is always interpreted as a single string.

Pass batch variables with spaces in them to powershell script?

I have a batch/powershell script that I use to make a list of all my movies and tv series but have run into a small problem, I can't have spaces in my batch variables when passing them to the powershell script.
I have tried all kinds of combos of ' and " around the variables without any luck.
My batch variables
SETLOCAL enabledelayedexpansion
SET "listfolder=%CD%\ContentList\"
SET "listname1=TVSeries"
SET "RootFolder1=\\SERVER\Storage\TV Series\"
SET "Folder1Width=70"
SET ScanFolder1=TRUE
My batch code to run and pass variables to powershell
"%listfolder%Script\Powershell.exe" -executionpolicy remotesigned -File "%listfolder%Script\folder1list.ps1" %RootFolder1% %listname1% %Folder1Width%
My powershell script code to set the variables.
param([String]$RootFolder1, [String]$listname1, [int32]$Folder1Width)
This is the error I get if I have space in %RootFolder1% path, without spaces all three variables get passed to powershell just fine.
C:\Users\hflat\Desktop\Folder Content List Maker v4.5\ContentList\Script\folder1list.ps1 : Cannot process argument
transformation on parameter 'Folder1Width'. Cannot convert value "TV" to type "System.Int32". Error: "Input string was
not in a correct format."
I found the solution here How to pass in a string with spaces into PowerShell?
What did work was to use a cmd wrapper with /S to unwrap outer quotes and removing my own path to powershell.exe
cmd /S Powershell.exe -executionpolicy remotesigned -File "%listfolder%Script\folder1list.ps1" "%RootFolder1%" "%listname1%" "%Folder1Width%"
I found the solution here How to pass in a string with spaces into PowerShell?
What did work was to use a cmd wrapper with /S to unwrap outer quotes and removing my own path to powershell.exe
cmd /S Powershell.exe -executionpolicy remotesigned -File "%listfolder%Script\folder1list.ps1" "%RootFolder1%" "%listname1%" "%Folder1Width%"

Run PowerShell, admin mode, in directory from shell context menu

I have created an Explorer Shell cascading context menu for opening PowerShell within a directory using the code shown below. (Note: I have a sample path hard-coded in the admin command for testing.)
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell\powershell.exe]
"MUIVerb"=""
"SubCommands"="powershell;powershell_admin"
"Icon"="PowerShell.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell]
#="Open PowerShell here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell\command]
#="powershell.exe -NoLogo -NoExit -Command Set-Location '%V'"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin]
#="Open PowerShell (admin) here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin\command]
#="powershell.exe -Command 'Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs'"
The non-admin command works perfectly. The admin command ignores everything in the ArgumentList.
If I open a PowerShell and execute the code within the single-quotes(') directly it works fine. E.G.:
Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs
But when executing from the context menu it opens in Admin mode but displays the logo and doesn't execute the Set-Location.
Thanks in advance
This is a cool utility.
I installed the Registry entries to experiment, and now that it's working I'll probably keep it.
In my experiments the window closed immediately, even before it brought up the UAC dialog, until I enclosed the outer command in escaped double quotes and the inner parameters in single quotes. Then it worked fine for every folder except those under Program Files. For that we need to enclose %v in single quotes, and double-escape the double-quotes surrounding it:
#="powershell.exe -command \"start-process powershell.exe -ArgumentList \\\"-NoLogo -NoExit -Command Set-Location '%v'\\\" -verb RunAs\""
I used %v - the context folder name, which seemed to be your original intention based on the menu label 'Open Powershell (admin) here'.
Debugging notes:
For experimentation it's a little easier to make the changes in Regedit.exe and let the export functionality add the escaping characters as necessary
In Windows 10 you can make changes directly within Regedit.exe and the context menu/action is updated immediately, which I confirmed by adding a timestamp to the menu label
In other versions of Windows it may be necessary to stop and restart explorer.exe

How can I run a PowerShell script with white spaces in the path from the command line?

So I've tried a bunch of different ways to run a PowerShell script from the command line and every single one returns an error.
Here is this path:
C:\Users\test\Documents\test\line space\PS Script\test.ps1
I've tried these:
powershell -File '"C:\Users\test\Documents\test\line space\PS Script\test.ps1"'
powershell "& ""C:\Users\test\Documents\test\line space\PS Script\test.ps1"""
Powershell "& 'C:\Users\test\Documents\test\line space\PS Script\test.ps1'"
Powershell -File 'C:\Users\test\Documents\test\line space\PS Script\test.ps1'"
I get all these errors:
& : The term 'C:\Users\test\Documents\test\line space\PS Script' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Processing -File ''C:\Users\test\Documents\test\line space\PS Script'' failed: The given path's format is not support ed. Specify a valid path for the -File parameter.
How can I fix this?
The -File parameter
If you want to run powershell.exe -File from the command line, you always have to set paths with spaces in double quotes ("). Single quotes (') are only recognized by PowerShell. But as powershell.exe is invoked (and hence the file parameter processed) by the command line, you have to use ".
powershell.exe -File "C:\Users\test\Documents\Test Space\test.ps1" -ExecutionPolicy Bypass
The -Command parameter
If you use the -Command parameter, instead of -File, the -Command content is processed by PowerShell. Hence you can - and in this case have to - use ' inside ".
powershell.exe -Command "& 'C:\Users\test\Documents\Test Space\test.ps1'" -ExecutionPolicy Bypass
The double quotes are processed by the command line, and & 'C:\Users\test\Documents\Test Space\test.ps1' is a command that is actually processed by PowerShell.
Solution 1 is obviously simpler.
Note that -Command is also the default parameter that is used, if you do not specify any.
powershell.exe "& 'C:\Users\test\Documents\Test Space\test.ps1'" -ExecutionPolicy Bypass
This would work, too.
The -EncodedCommand parameter
You can encode your command as Base64. This solves many "quoting" issues and is sometimes (but not in your case though) the only possible way.
First you have to create the encoded command
$Command = "& 'C:\Users\test\Documents\Test Space\test.ps1'"
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Command))
And then you can use the the -EncodedCommand parameter like this
powershell.exe -EncodedCommand JgAgACcAQwA6AFwAVQBzAGUAcgBzAFwAdABlAHMAdABcAEQAbwBjAHUAbQBlAG4AdABzAFwAVABlAHMAdAAgAFMAcABhAGMAZQBcAHQAZQBzAHQALgBwAHMAMQAnAA== -ExecutionPolicy Bypass
Try this:
& "C:\Users\test\Documents\test\line space\PS Script\test"
In your examples, you're mixing quotes and double quoting for no reason.
IF EXIST "C:\Users\test\Documents\test\line space\PS Script\test.ps1" (
powershell -ExecutionPolicy Unrestricted -File "C:\Users\test\Documents\test\line space\PS Script\test.ps1"
)
In case you use parameters you can do as follows.
powershell.exe -command "& {&'C:\A B C\foo.ps1' param1 param2}"
Thanks at this point to a blog post by Hesham A. Amin :-)
I needed to pass a parameter with spaces.
I am dragging and dropping a file onto a batch file, and the file is off on the server with spaces in the path and/or file name. After testing the above answers, I got this to work. Note I am changing to the working directory prior to starting the PowerShell executable.
Batch file:
pushd O:\Data\QuickList
start powershell -noexit -Command ".\QuickList.ps1 -datafile '%1'"
popd