Spaced paths, msbuild, and psake - powershell

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">

Related

How to pass a variable to new console window in Powershell

I want to pass a variable to a new console, but I don't know how.
$server = "server_name"
Start-Process Powershell {
$host.ui.RawUI.WindowTitle = “Get-Process”
Invoke-Command -ComputerName $server -ScriptBlock {
Get-Process
}
cmd /c pause
}
Error message:
Invoke-Command : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again
Start-Process only accepts (one or more) strings as arguments, not a script block ({ ... }).
While a script block is accepted, it is simply stringified, which results in its verbatim content being passed as the argument (sans { and }), which means that $server is retained as-is (not expanded), and the child process that runs your command doesn't have a variable by that name defined, causing Invoke-Command to fail due not receiving a value for -ComputerName.
Therefore, in order to incorporate variable values from the caller's scope, you must use string interpolation, using an expandable (double-quoted) string ("...") that encodes all arguments:[1]
$server = "server_name"
# Parameters -FilePath and -ArgumentList are positionally implied.
# For the resulting powershell.exe call, -Command is implied.
Start-Process powershell "
$host.ui.RawUI.WindowTitle = 'Get-Process'
Invoke-Command -ComputerName $server -ScriptBlock { Get-Process }
pause
"
A computer name ($server, in your case) doesn't contain spaces, but any variable values that do would require embedded enclosing quoting inside the overall "..." string, such as \`"$someVar\`" (`" escapes an " inside a "..." string in PowerShell, and the \ is additionally needed to escape the resulting verbatim " for the PowerShell CLI, powershell.exe).
For full robustness, additionally enclose the entire string value (implied -Command argument) in embedded "..." quoting ("`"...`"").
You can make this a bit easier by using the here-string form of an expandable string (#"<newline>...<newline>"#), inside of which you don't need to escape " chars.
Example of a fully robust call:
$someVar = 'A value with spaces'
Start-Process powershell #"
-NoProfile -Command "
# Echo the value of $someVar
Write-Output \"$someVar\"
pause
"
"#
Note the use of -NoProfile before -Command, which suppresses loading of the profile files, which can speed up the call and makes for a more predictable execution environment.
[1] Technically, -ArgumentList accepts an array of arguments, and while passing the pass-through arguments individually may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer.
if you want to use param-
info
param([type]$p1 = , [type]$p2 = , ...)
or:
info
param(
[string]$server
)
Write-Host $a
./Test.ps1 "your server name"

Powershell any way to hide yes and no popup while adding registry [duplicate]

Powershell seems to drop empty string arguments when passed to a command. I have this code
PS D:\> $b.name = "foo bar"
PS D:\> ./echoargs $b.name
Arg 0 is D:\echoargs.exe
Arg 1 is foo bar
PS D:\> $b.name = ""
PS D:\> ./echoargs $b.name
Arg 0 is D:\echoargs.exe
You can assume that $b has a 'name' member. How can i pass this as an argument to the exe even when the value is an empty string. I've tried using the call operator with no success.
If you want an empty string to appear try escaped quotes around the argument like so:
PS> $b = [psobject]#{name = ''}
PS> echoargs `"$($b.Name)`"
Arg 0 is <>
Command line:
"C:\Users\Keith\Pscx\Trunk\Src\Pscx\bin\Release\Apps\EchoArgs.exe" ""
Note that I tested this on V3 so I'm not sure if the behavior will be exactly the same on V2.
Try to pass an empty single quote string enclosed in double quotes, or vice versa.
./echoargs $b.name, "''"
or
./echoargs $b.name, '""'
This should also work:
./echoargs [String]::Empty

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

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

Piping from a variable instead of file in Powershell

Is ther any way in Powershell to pipe in from an virable instead of a file?
There are commands that I need to pipe into another command, right now that is done by first creating a file with the additional commands, and then piping that file into the original command. Code looks somehting like this now:
$val = "*some command*" + "`r`n" + "*some command*" + "`r`n" + "*some command*"
New-Item -name Commands.txt -type "file" -value $val
$command = #'
db2cmd.exe /C '*custom db2 command* < \Commands.txt > \Output.xml'
'#
Invoke-Expression -Command:$command
So instead of creating that file, can I somehow just pipe in $val insatead of Commands.txt?
Try this
$val = #("*some command*1","*some command2*","*some command3*")
$val | % { db2cmd.exe /C $_ > \Output.xml }
You should be able to pipe in from $val provided you use Write-Output or its shorthand echo, but it may also be worth trying passing the commands directly on the command line. Try this (and if it doesn't work I can delete the answer):
PS C:\> filter db2cmd() { $_ | db2cmd.exe ($args -replace '(\\*)"','$1$1\"') }
PS C:\> $val = #"
>> *custom db2 command*
>> *some command*
>> *some command*
>> *some command*
>> "#
>>
PS C:\> db2cmd /C $val > \Output.xml
What happens here is that Windows executables receive their command line from a single string. If you run them from cmd.exe you cannot pass newlines in the argument string, but Powershell doesn't have that restriction so with many programs you can actually pass multiple lines as a single argument. I don't know db2cmd.exe so it might not work here.
The strange bit of string replacement is to handle any double quotes in the arguments: Powershell doesn't quote them and the quoting rules expected by most exe files are a bit bizarre.
The only limitation here would be that $val must not exceed about 32,600 characters and cannot contain nulls. Any other restrictions (such as whether non-ascii unicode characters work) would depend on the application.
Failing that:
echo $val | db2cmd.exe /C '*custom db2 command*' > \Output.xml
may work, or you can use it in combination with the filter I defined at the top:
echo $val | db2cmd /C '*custom db2 command*' > \Output.xml