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

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

Related

Powershell: gpg command parameters with embedded double quotes

I'm trying to call gpg2 from a Powershell script. I need to pass parameters with embedded quotes but I get some very odd behavior when I look at the results from echoargs or the executable directly.
$Passphrase = "PassphraseWith!$#" #don't worry, real passphrase not hardcoded!
$Filename = "\\UNC\path\with\a space\mydoc.pdf.pgp"
$EncyptedFile = $Filename -replace "\\", "/"
$DecryptedFile = $EncyptedFile -replace ".pgp" , ""
$args = "--batch", "--yes", "--passphrase `"`"$PGPPassphrase`"`"", "-o `"`"$DecryptedFile`"`"", "-d `"`"$EncyptedFile`"`""
& echoargs $args
& gpg2 $args
gpg requires me to use double quotes for the passphrase because it has symbols and for the paths because of a space (confirmed this works when I run a sample single command directly from command prompt). Also, gpg wants UNC paths with forward slashes (confirmed this works too).
As you can see I am trying to wrap the passphrase and file paths with paired escaped double quotes because echoargs seems to indicate the outer quotes are being stripped off. Here is what i get from echoargs:
Arg 0 is <--batch>
Arg 1 is <--yes>
Arg 2 is <--passphrase "PassphraseWith!$#">
Arg 3 is <-o "//UNC/path/with/a space/mydoc.pdf">
Arg 4 is <-d "//UNC/path/with/a space/mydoc.pdf.pgp">
Command line:
"C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\PSCX\Apps\EchoArgs.exe" --batch --yes "--pass
phrase ""PassphraseWith!$#""" "-o ""//UNC/path/with/a space/mydoc.pdf""" "-d ""//UNC/path/with/a space/mydo
c.pdf.pgp"""
However, gpg2 gives the following result (whether run from ISE or PS directly):
gpg2.exe : gpg: invalid option "--passphrase "PassphraseWith!$#""
If I try & gpg2 "$args" to convert the array to a string then I get the following similar result:
gpg2.exe : gpg: invalid option "--batch --yes --passphrase "PassphraseWith!$#"
Any ideas on this one?
#PetSerAl's solution: You need to tokenize the flag/parameter and its value, so split out into two elements in the array:
"--passphrase", "`"$Passphrase`""
not combined as:
"--passphrase `"`"$Passphrase`"`""
Note that regular Powershell escaping quotes using backticks works fine here.
Full example below:
$Passphrase = "PassphraseWith!$#" #don't worry, real passphrase not hardcoded!
$Filename = "\\UNC\path\with\a space\mydoc.pdf.pgp"
$EncyptedFile = $Filename -replace "\\", "/"
$DecryptedFile = $EncyptedFile -replace ".pgp" , ""
$params = "--batch", "--quiet", "--yes", "--passphrase", "`"$Passphrase`"", "-o", "`"$DecryptedFile`"", "-d", "`"$EncyptedFile`""
& echoargs $params
& gpg2 $params

Passing new line from command line to PowerShell

How to pass a new line character from the command line to PowerShell?
// MyScript.ps1:
[CmdletBinding()]
Param(
[Parameter()] $Param
)
Write-Host $Param
// Command line (not PowerShell - cmd.exe!)
powershell.exe -File MyScript.ps1 -Param "First`nSecond"
Does not work. Also using the command line's new line character \n does not work. So how to pass a new line tothe command line?
A workaround would be to use any other character, e.g. \n in the command line, and replace it within PowerShell:
$x = $param.Replace("\n", "`n")
This works, however, it is of course a hack and not my preferred solution.
There is a special character as in First Line◙Second Line.
When calling from CMD with a variable, I had to pass the parameter as a literal (using ' instead of ").
Sample pstest.ps1:
Write-Host "Without Replace:"
Write-Host $args[0]
Write-Host #Blank Space for Readability
Write-Host "With Replace:"
Write-Host $args[0].Replace("\n", "`n")
Call from CMD and results:
> set "tmpvar=blar1 de \nblar2"
> powershell.exe .\pstest.ps1 '%tmpvar%'
Without Replace:
blar1 de \nblar2
With Replace:
blar1 de
blar2
You need a carriage return and line feed:
`r`n
So this should work:
powershell.exe -File MyScript.ps1 -Param "First`r`nSecond"

Invoke-Command Powershell parameter issue

I need to pass a comma delimited parameter to a batch file via powershell and can't seem to get it to work. Here's how I call the batch file if I call it directly in powershell:
PS C:\Users\Mike> type zz.cmd
#echo off
echo/* = [%*}
echo/1 = [%1]
echo/2 = [%2]
echo/3 = [%3]
pause
PS C:\Users\Mike> cmd /c zz "q,w,e"
* = [q,w,e}
1 = [q]
2 = [w]
3 = [e]
Press any key to continue . . .
If I use cmd /c zz """q,w,e""" or cmd /c zz '"q,w,e"' I will get "q,w,e" for arg 1. This is good. However, I must call powershell using Invoke-Command. When doing this, the script doesn't work:
powershell.exe Invoke-Command -ScriptBlock { "cmd /c E:\\npoess\\oo\\WoD\\zzz" '"q,w,e"'}
Any idea how to get the powershell call from the command prompt to get "q,w,e" as one parameter to the batch file?
Thanks,
-Mike
this invoke-command without all the quotes works for passing a for me :
I think that the quotes around the variables passes the set as a string instead of separate values.
Invoke-Command -ScriptBlock {
cmd /c C:\scripts\zz.cmd q,w,e
}

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.

Spaced paths, msbuild, and psake

Related question here.
This works properly for compiling an mvc3 application.
task Compile
{
$config = $script:siteConfig.config
exec { & "C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" $webproject_path `
/p:Configuration=$config /p:WebProjectOutputDir="$publish_dir" `
/p:Outdir="$out_dir" /p:CleanWebProjectOutputDir=False `
/T:_WPPCopyWebApplication /T:ResolveReferences /verbosity:quiet /nologo }
}
All of those path variables are script properties. However, when spaces are introduced in those calculated paths (e.g. the project is moved from C:\Projects\ to C:\Users\ASDFG1\Documents\Visual Studio 2010\Projects) msbuild thinks there's multiple project files. This makes sense but I have to be missing something, getting a parsed variable into quotes shouldn't be this hard.
Variations tried
exec { Invoke-Expression "& C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe '$webproject_path' /p:Configuration=$config /p:WebProjectOutputDir='$publish_dir' /p:Outdir='$out_dir' /p:CleanWebProjectOutputDir=False /T:_WPPCopyWebApplication /T:ResolveReferences /verbosity:quiet /nologo" }
exec { C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "`"$webproject_path`"" `
/p:Configuration=$config /p:WebProjectOutputDir="`"$publish_dir`"" `
/p:Outdir="`"$out_dir`"" /p:CleanWebProjectOutputDir=False `
/T:_WPPCopyWebApplication /T:ResolveReferences /verbosity:quiet /nologo }
Using EchoArgs.exe to reproduce the problem, we see that quotes are not being passed to the executable as desired:
PS> $publish_dir = 'C:\Users\Documents\Visual Studio 2010\Projects'
PS> ./echoargs /p:WebProjectOutputDir="$publish_dir"
Arg 0 is </p:WebProjectOutputDir=C:\Users\Documents\Visual Studio 2010\Projects>
PS> ./echoargs /p:WebProjectOutputDir="`"$publish_dir`""
Arg 0 is </p:WebProjectOutputDir=C:\Users\Documents\Visual>
Arg 1 is <Studio>
Arg 2 is <2010\Projects>
Using the backslash-escaping option from this answer, we can preserve the variable expansion and the enclosing quotes:
PS> ./echoargs /p:WebProjectOutputDir=\`"$publish_dir\`"
Arg 0 is </p:WebProjectOutputDir="C:\Users\Documents\Visual Studio 2010\Projects">
Here, the backticks tell PowerShell to treat the quote characters as literal values, and the backslash tells the call invocation to preserve the quotes.
Alternatively, we could stick with a single level of escaping by evaluating the full argument beforehand, instead of inlining the $publish_dir variable:
PS> $publishArg = '/p:WebProjectOutputDir=\"{0}\"' -f $publish_dir
PS> ./echoargs $publishArg
Arg 0 is </p:WebProjectOutputDir="C:\Users\Documents\Visual Studio 2010\Projects">