Powershell call -Command property with spaces - powershell

I have script D, C and B :
ScriptD.ps1 ← caller
ScriptC.ps1 ← script path without space
Script B.ps1 ← script path with space
Script B & ScriptC:
Param([int]$Major, [int]$Minor, [String[]]$Output = #())
Write-Host "`n_______________________________________________________________________________________`n"
Write-Host "Script C:`nMajor = $Major ($($Major.GetType()))`nMinor = $Minor ($($Minor.GetType()))`nOutput = $Output (Type: $($Output.GetType()), Length: $($Output.Length))" -ForegroundColor Green
foreach($string in $Output) {
Write-Host "`t- $string"
}
Then I call ScriptC.ps1, Script B.ps1 from ScriptD.ps1:
$cmd1 = "C:\ScriptTest\ScriptC.ps1 -Major 1 -Minor 2 -Output A,B"
powershell -Command $cmd1 #WORK
$cmd2 = "C:\ScriptTest\Script B.ps1 -Major 1 -Minor 2 -Output A,B"
powershell -Command $cmd2 #DON'T WORK
$cmd2 = "'C:\ScriptTest\Script B.ps1' -Major 1 -Minor 2 -Output A,B" #DON'T WORK
$cmd2 = '"C:\ScriptTest\Script B.ps1" -Major 1 -Minor 2 -Output A,B' #DON'T WORK
$cmd2 = $('"{0}"' -f ("C:\ScriptTest\Script B.ps1")) + " -Major 1 -Minor 2 -Output A,B" #DON'T WORK
If there are spaces in the script path or in a variable the call don't work. Adding single quotes will not solve that problem.
What's wrong?
How could I use the -Command parameter with spaces in path and passed variables?

Don't do what you're doing. There is zero reason to invoke PowerShell scripts from another PowerShell script the way you do.
Change
$cmd1 = "C:\ScriptTest\ScriptC.ps1 -Major 1 -Minor 2 -Output A,B"
powershell -Command $cmd1
$cmd2 = "C:\ScriptTest\Script B.ps1 -Major 1 -Minor 2 -Output A,B"
powershell -Command $cmd2
into
C:\ScriptTest\ScriptC.ps1 -Major 1 -Minor 2 -Output A,B
& "C:\ScriptTest\Script B.ps1" -Major 1 -Minor 2 -Output A,B
and everything will work.
Even if you needed to start the other scripts in a separate process you wouldn't use the approach you chose, but something like this:
$paramsc = '-File', 'C:\ScriptTest\ScriptC.ps1',
'-Major', 1, '-Minor', 2, '-Output', 'A,B'
Start-Process 'powershell.exe' -ArgumentList $paramsc
$paramsb = '-File', '"C:\ScriptTest\Script B.ps1"',
'-Major', 1, '-Minor', 2, '-Output', 'A,B'
Start-Process 'powershell.exe' -ArgumentList $paramsb
The ugly nested quotes in the second example are required because the script path contains a space and must thus be in double quotes for the creation of the external process. However, I doubt that you'll be able to pass output variables back to the caller across process boundaries.

Related

How to call a powershell script in vbscript?

I'm trying to call a powershell script in-between one of my project steps in Visual Build.
I've created a new script within visual build and having that call my powershell script.
I selected vbscript and this is the code within the script:
Dim paths, source1
paths = "C:\Dev\"
source1 = "\\10.156.3.14\win"
sCmd = "powershell.exe -noexit -ExecutionPolicy Unrestricted -file C:\Dev\downloadfiles.ps1 -target """ & paths & """ -source """ & source1 & """"
Set xShell = CreateObject("Wscript.Shell")
rReturn = xShell.Run(sCmd, 0, true)
The script timeouts my build.
The powershell script works fine when ran through the console.
Is there something I'm missing?
download.files.ps1 paramaters:
param (
[string[]]$target,
[string[]]$source
)
Additionally is there any way I could see the console output whilst it's running. Even with "-noexit" I'm seeing nothing.
Update --
The first part of the script runs and returns some of the relevant files.
Although the part that takes in the parameters are not functioning.
This seems to be the better alternative as the script is now taking in parameters :
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -noexit -ExecutionPolicy Unrestricted -file .\downloadfiles.ps1 -target """ & paths & """ -source """ & source1 & """")
Follow up question. How would I go about passing in a string arrray through the vbscript call?
e.g
$target = #('c:\dev','d:\lib')
When using -File, I'm afraid all array elements in $target will be forwarded to the PowerShell script as a single string.
Try
VbScript
Option Explicit
'I hate writing """something""" or Chr(34) & "something" & Chr(34)
'all the time, so I use this little helper function
Private Function Quote(text)
Quote = Chr(34) & text & Chr(34)
End Function
Dim paths, source1, script, sCmd, objShell
'join the quoted path elements with a semi-colon
paths = Join(Array(Quote("C:\Dev"), Quote("D:\lib")), ";")
source1 = Quote("\\10.156.3.14\win")
script = Quote("D:\Test\downloadfiles.ps1")
sCmd = "powershell.exe -NoExit -NoLogo -ExecutionPolicy Unrestricted -File " & script & " -target " & paths & " -source " & source1
Set objShell = CreateObject("Wscript.Shell")
objShell.run(sCmd)
Test with PowerShell
downloadfiles.ps1
param (
[string[]]$target,
[string[]]$source
)
$target = $target | ForEach-Object { $_ -split ';' }
$source = $source | ForEach-Object { $_ -split ';' }
Write-Host "Elements in target: $($target.Count)"
Write-Host "Elements in source: $($source.Count)"
Write-Host
for ($i = 0; $i -lt $target.Count; $i++) {
'$target[{0}]: {1}' -f $i, $target[$i]
}
Write-Host
for ($i = 0; $i -lt $source.Count; $i++) {
'$source[{0}]: {1}' -f $i, $source[$i]
}
Output in PowerShell console:
Elements in target: 2
Elements in source: 1
$target[0]: C:\Dev
$target[1]: D:\lib
$source[0]: \\10.156.3.14\win

Pass string array through nested Invoke-Expression

My usecase consists 3 Scripts:
Script A.ps1 ← start script
Script B.ps1 ← script in the middle
Script C.ps1 ← target script to execute
Constraints: process spaces in path string[] array variables
Script A.ps1:
#SCRIPT EXPRESSION (TO PASS AS ARGUMENT)
$scriptC = "Script C.ps1"
$major = 1
$minor = 2
[string[]]$output = "Output A.txt", "Output B.txt", "Output C.txt"
$scriptExpression = 'powershell -File \"{0}\" -Major {1} -Minor {2} -Output {3}' -f ($scriptC, $major, $minor, $output)
Write-Host $scriptExpression -ForegroundColor Green
#SCRIPT EXPRESSION (TO CALL)
$scriptB = "Script B.ps1"
$config = "Debug"
$flag = 1
[string[]]$array = "String with space A", "String with space B"
$params = #("-File", "$scriptB", "-Config", $config, "-Flag", "$flag", "-ScriptExpression", $scriptExpression, "-Array", $array)
Write-Host "& powershell $params" -ForegroundColor Green
& powershell $params
Script B.ps1:
Param([string]$Config, [int]$Flag, [string]$ScriptExpression, [string[]]$Array)
Write-Host "`n_______________________________________________________________________________________`n"
Write-Host "Script B:`nConfig = $Config ($($Config.GetType()))`nFlag = $Flag ($($Flag.GetType()))`nScriptExpression = $ScriptExpression ($($ScriptExpression.GetType()))`nArray = $Array (Type: $($Array.GetType()) Length: $($Array.Length) <- WRONG LENGTH (2))" -ForegroundColor Yellow
Write-Host "`nCall Script C!" -ForegroundColor Yellow
Invoke-Expression $ScriptExpression
Script C.ps1:
Param([int]$Major, [int]$Minor, [string[]]$Output = #())
Write-Host "`n_______________________________________________________________________________________`n"
Write-Host "Script C:`nMajor = $Major ($($Major.GetType()))`nMinor = $Minor ($($Minor.GetType()))`nOutput = $Output (Type: $($Output.GetType()), Length: $($Output.Length)) <- WRONG EVERYTHING" -ForegroundColor Green
Output:
powershell -File \"Script C.ps1\" -Major 1 -Minor 2 -Output System.String[]
& powershell -File Script B.ps1 -Config Debug -Flag 1 -ScriptExpression powershell -File \"Script C.ps1\" -Major 1 -Minor 2 -Output System.String[] -Array System.String[]
_______________________________________________________________________________________
Script B:
Config = Debug (string)
Flag = 1 (int)
ScriptExpression = powershell -File "Script C.ps1" -Major 1 -Minor 2 -Output System.String[] (string)
Array = String with space A String with space B (Type: string[] Length: 1 ← WRONG LENGTH (2))
Call Script C!
_______________________________________________________________________________________
Script C:
Major = 1 (int)
Minor = 2 (int)
Output = System.String[] (Type: string[], Length: 1) ← WRONG EVERYTHING
How is it possible to pass these string[] arrays with spaces to another script?
I could achieve to call a Script C.ps1 from Script A.ps1 over Script B.ps1 with: (Content of Script B and C is in question)
$output = #("Output A.txt", "Output B.txt", "Output C.txt")
$array = #("String with space A", "String with space B")
#INVOKE EXPRESSION SCRIPT C
$varScript = $("'{0}\Script C.ps1'" -f ($PSScriptRoot))
$varMajor = 1
$varMinor = 2
$varOutput = "'" + $($output -join "','") + "'"
$expression = "& $varScript -Major $varMajor -Minor $varMinor -Output $varOutput"
$expression = """{0}""" -f ($expression) #WRAP IN DOUBLE QUOTES
#INVOKE EXPRESSION SCRIPT B
$varScript = $("'{0}\Script B.ps1'" -f ($PSScriptRoot))
$varConfig = "Debug"
$varFlag = 1
$varArray = "'" + $($array -join "','") + "'"
$call = "& $varScript -Config $varConfig -Flag $varFlag -ScriptExpression $expression -Array $varArray"
Invoke-Expression $call
Output:
Script B:
Config = Debug (string)
Flag = 1 (int)
ScriptExpression = & 'C:\ScriptTest\Script C.ps1' -Major 1 -Minor 2 -Output 'Output A.txt','Output B.txt','Output
C.txt' (string)
Array = String with space A String with space B (Type: string[] Length: 2)
Call Script C!
_______________________________________________________________________________________
Script C:
Major = 1 (int)
Minor = 2 (int)
Output = Output A.txt Output B.txt Output C.txt (Type: string[], Length: 3)
- Output A.txt
- Output B.txt
- Output C.txt

Different script results from identical shortcut and console input

I am trying to input 2 args from a shortcut and automatically assign them like this:
powershell.exe -NoExit -NoProfile -File d:\scripts\swap-parms.ps1 A 7
This script works perfectly when run from the console, but not from a shortcut!
[int]$digit = 6 #Default values.
[String]$variable = 'X'
Write-Host $args[0].GetType().Name #Shows both as String from hortcut!
Write-Host $args[1].GetType().Name
switch($args) { #Allows any order of input parameters and assigns.
{$_.GetType().Name -eq 'Int32'} {$digit = $_ ; continue}
{$_.GetType().Name -eq 'String'} {$variable = $_}
}
$digit
$variable
I know I can do this with named parameters like '-var B -dig 2' but, as it works perfectly from the console I can't understand why it fails with identical args from the shortcut. Ideas please?
Use -command instead of -file. For proof:
==> powershell -noprofile -file D:\PShell\SO\swap-parms.ps1 A 33
String
String
6
33
==> powershell -noprofile -command D:\PShell\SO\swap-parms.ps1 A 33
String
Int32
33
A
==>

How to correctly escape spaces and backslashes in command line arguments?

I had some issues passing an array of strings to a command in PowerShell, so I'm debugging my script. I'm using the EchoArgs.exe program found in the PowerShell Community Extension Project (PSCX).
If I execute this script:
Import-Module Pscx
cls
$thisOne = 'this_one\';
$secondOne = 'second one\';
$lastOne = 'last_one'
$args = $thisOne `
, "the $secondOne" `
, "the_$lastOne"
EchoArgs $args
I get this result:
Arg 0 is <this_one\>
Arg 1 is <the second one" the_last_one>
Command line:
"C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" this_one\ "the second one\" the_last_one
It seems that if a string contains spaces, the last backslash escapes the double quote. In fact all seems working if I escape only that backslash:
Import-Module Pscx
cls
$thisOne = 'this_one\';
$secondOne = 'second one\\';
$lastOne = 'last_one'
$args = $thisOne `
, "the $secondOne" `
, "the_$lastOne"
EchoArgs $args
with this result:
Arg 0 is <this_one\>
Arg 1 is <the second one\>
Arg 2 is <the_last_one>
Command line:
"C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" this_one\ "the second one\\" the_last_one
Is there a "smart" way in PowerShell (i.e. a cmdlet) to escape any string in order to avoid such issues?
Try using Start-Process instead. It has an $Arguments parameter that would suit this better.
See here: PowerShell - Start-Process and Cmdline Switches

start-process in PowerShell 3.0 script doesn't work but it was working in 2.0

I upgraded our PS version to 3.0 and some of our scripts stopped working. After a lot of debugging I realized there is an issue with the Start-Process command.
Basically, when I run the Start-Process directly in the PowerShell cmd it does run the program with the correct arguments. However, when I run the script, it won't give any error but the program will not run.
The script is a bit long, but this is the way I can test the snippet that's failing.
$SERVER = 'servername'
$PORT = 'xxxx'
$TPath = 'E:\epicor\PowerShell\export\POAutomation\'
$User = 'username'
$Psw = 'password'
$Import = 'PO Combined'
$file = $TPath + 'AutomaticPOHeaders.csv'
$DMTPATH = 'E:\epicor\Epicor905\Client\dmt.exe'
$Param = "-ArgumentList `'-user=`"$User`" -pass=`"$Psw`" -server=$SERVER -port=$PORT -import=`"$Import`" -source=`"$file`" -add=true -update=false -noui=true`'"
Start-Process $DMTPATH $Param -Wait
"Finished"
I even wrote to a log file to check if the $Param string is well formed and if the Start-Process command is also well written. When I copy paste the strings in the log file into the PS command line, they run successfully.
I've been stuck with this more than 4 hours now.
Thanks in advance.
i dont know what dmt is waiting for but this command runs successfully on ps V3.
are you sure about you argumentlist parameter ? and seems to be a mess with your quotes
slight changes : use echoargs.exe instead of DMT and add a switch to not open a new window :
$DMTPATH = 'echoargs.exe'
$Param = "-ArgumentList `'-user=`"$User`" -pass=`"$Psw`" -server=$SERVER -port=$PORT -import=`"$Import`" -source=`"$file`" -add=true -update=false -noui=true`'"
Start-Process -nonewwindow $DMTPATH $Param -Wait
"Finished"
results :
Arg 0 is <-ArgumentList>
Arg 1 is <'-user=username>
Arg 2 is <-pass=password>
Arg 3 is <-server=servername>
Arg 4 is <-port=xxxx>
Arg 5 is <-import=PO Combined>
Arg 6 is <-source=E:\epicor\PowerShell\export\POAutomation\AutomaticPOHeaders.csv>
Arg 7 is <-add=true>
Arg 8 is <-update=false>
Arg 9 is <-noui=true'>
Command line:
"C:\Windows\EchoArgs.exe" -ArgumentList '-user="username" -pass="password" -server=servername -port=xxxx -import="PO Combined" -source="E:\epicor\PowerShell\export\POAutomation\AutomaticPOH
aders.csv" -add=true -update=false -noui=true'
Finished
Can you try to start dmt from cmd.exe ? something like :
$p=#("/C";"dmt.exe";"-user'test'" ....)
Start-Process -NoNewWindow cmd.exe $p
I run into the same problem. I noticed if the -noui=true is removed, it seems to work.