Search code examples
powershellvbscript

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!


Solution

  • 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.