A dll can be invoked in Powershell; it takes several parameters, and I must run it several times while most of them stay the same, like this:
dotnet C:\App\App.dll AppCmd --MySwitch --Param1 ab --Param2 cd --Param30 xx
dotnet C:\App\App.dll AppCmd --MySwitch --Param1 ab --Param2 cd --Param30 yy
dotnet C:\App\App.dll AppCmd --MySwitch --Param1 ab --Param2 cd --Param30 zz --Param31 x:y y:z
I'd like to 'splat' the common parameters and manually write out only what is unique to each invocation, like:
$CommonArgs = #{
Param1 = 'ab',
Param2 = 'cd'
...
}
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 xx -- #CommonArgs
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 yy -- #CommonArgs
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 zz --Param31 x:y y:z -- #CommonArgs
The App stops complaining about missing required parameters with the above syntax but it returns an error: Unhandled Exception: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. I'm not sure whether that error message comes from the Application or Powershell itself.
I figured out splatting within Powershell functions and I read the -- should pass the parameters along to the dotnet call but apparently I'm still missing something. What is it?
Using hashtable-based splatting with external programs rarely works, because of the very specific format that PowerShell translates the hashtable entries to: -<key>:<value> - this won't work in your case.
For external programs, array-based splatting is the better choice - and, in fact, you don't even need the splatting sigil (# instead of $) at all and you can just pass an array variable as-is:
$CommonArgs = #( # array, not hashtable
'--Param1', 'ab',
'--Param2', 'cd'
# ...
)
# ...
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 zz --Param31 x:y $CommonArgs y:z
Note how each argument - irrespective of whether it represents a parameter name or a value - must be its own array element.
PowerShell simply space-concatenates the array elements when building the command line to use for invocation behind the scenes, applying double-quoting around elements with embedded spaces as needed.
I've omitted --, because it would be passed through to the external program - not sure if that's the intent (only for PowerShell-native commands is it removed by the parameter binder).
Troubleshooting tip:
The bottom section of this answer shows how to build a helper executable that can be used to echo the exact command line it was given, as well as how it parsed it into individual arguments (using .NET's parsing rules, which are based on Microsoft's C/C++ compiler).
Too long for a comment, and I have no idea if this would work, but perhaps you can use a small helper function to convert the common arguments from the splatting Hashtable into commandline syntax like
function Format-CliArguments {
param (
$prefix = '--'
)
$numTypes = 'byte','sbyte','int','int16','int32','int64','uint16','uint32',
'uint64','long','float','single','double','decimal'
$cliCommand = for ($i = 0; $i -lt $args.Count; $i += 2) {
if ($numTypes -contains $args[$i + 1].GetType().Name) {
'{0}{1} {2}' -f $prefix, $args[$i].Trim("-:"), $args[$i + 1]
}
else {
'{0}{1} "{2}"' -f $prefix, $args[$i].Trim("-:"), $args[$i + 1]
}
}
$cliCommand -join ' '
}
# you can make this [ordered]#{..} if the sequence is important
$CommonArgs = #{
Parameter1 = 'ab'
Parameter2 = 123
Parameter3 = 'zz'
}
$standardArgs = Format-CliArguments #CommonArgs
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 xx $standardArgs
dotnet C:\App\App.dll AppCmd --MySwitch --Param30 yy $standardArgs
etc.
Related
I have a PowerShell script that I trigger from Bamboo, the script basically accepts 3 string parameters:
Param([string]$language, [string]$architecture ="x64", [string]$version)
$x64Path = "some path..."
$x86Path = "some path.."
$arguments = $language,$architecture,$version
If($architecture -eq "x64") {
Write-Host "Executing:" $x64Path" at x64 using" $language "and" $version
# & $x64Path $arguments
}
Else {
Write-Host "Executing:" $x86Path "at x86 using" $language "and" $version
# & $x86Path $arguments
}
if I run the PowerShell script from the ISE, for example:
.\MyScript.ps1 "JPN" "x64" "AB 6.0 R"
I will correctly get this output:
Executing: some path... at x64 using JPN and AB 6.0 R
I have created a plan in Bamboo with the following:
Variables
Task
I have tried adding the variables (argument) as:
"${bamboo.Language}" "${bamboo.Architecture}" ${bamboo.Version}
and aso as:
"${bamboo.Language}" "${bamboo.Architecture}" "${bamboo.Version}"
But I always get the same output:
Executing: some path... at x64 using JPN and AB
Notice that the last variable is cut out (I don't get the full AB 6.0 R)
Is this a bug in Bamboo, or something I'm doing wrong? or is there a workaround it?
PS. To achieve this run in Bamboo (by giving the variable values), I'm running the plan as follows:
I suspect the quotes get stripped after variable replacement in bamboo (ie. before the arguments are passed to powershell).
Add an inner set of single-quotes to the argument that has spaces:
"${bamboo.Language}" "${bamboo.Architecture}" "'${bamboo.Version}'"
I am trying to write a script that basically executes a cli command like:
snmpget -v 1 -c xxxxxx-Ovq xx.xx.xx.xxx .1.3.6.1.2.1.1.8.0
where xxxxx is a password and xx.xx.xx.xxx and IP that normally returns:
49:22:12:15.00
My script is:
#!/usr/local/bin/perl
#snmpget -v 1 -c xxxxx -Ovq xx.xx.xx.xxx .1.3.6.1.2.1.1.8.0
$SNMP_GET_CMD = "snmpget -v1 -c xxxxx-Ovq";
$SNMP_TARGET = "xx.xx.xx.xxx";
my $sysORLastChange = '${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0';
chomp($sysORLastChange);
print("${SNMP_TARGET} as an Input Line Reading of ${sysORLastChange}\n");
and the output is:
xx.xx.xx.xxx as an Input Line Reading of ${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0
It should return the following:
xx.xx.xx.xxx as an Input Line Reading of 49:22:12:15.00
Is there any problem with the syntax i used in the script?
In Perl, use double-quotes to interpolate another variable into a string. When you define $sysORLastChange using other variables within a single-quoted string like this:
my $sysORLastChange = '${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0';
...the string is being assigned verbatim (ie. the inner variables aren't being expanded).
To correct this, assign to the variable using double-quotes, which will interpolate the inner variables into their values:
my $sysORLastChange = "${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0";
If you want to actually execute the string, you can use the qx() operator, aka the "backtick" style quotes:
my $sysORLastChange = qx(${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0);
# or...
my $sysORLastChange = `${SNMP_GET_CMD} ${SNMP_TARGET} .1.3.6.1.2.1.1.8.0`;
See Perl Quote and Quote-like Operators in perlop.
EDIT: I've changed the code here to a simple test case, rather than the full implementation where this problem is arising.
I am trying to call one Powershell script from another, but things aren't working out as I'm expecting. As I understand things, the "&" operator is supposed to expand arrays into distinct parameters. That's not happening for me.
caller.ps1
$scriptfile = ".\callee.ps1"
$scriptargs = #(
"a",
"b",
"c"
)
& $scriptfile $scriptargs
callee.ps1
Param (
[string]$one,
[string]$two,
[string]$three
)
"Parameter one: $one"
"Parameter two: $two"
"Parameter three: $three"
Running .\caller.ps1 results in the following output:
Parameter one: a b c
Parameter two:
Parameter three:
I think that the problem I'm experiencing is $scriptargs array is not expanded, and is rather passed as a parameter. I'm using PowerShell 2.
How can I get caller.ps1 to run callee.ps1 with an array of arguments?
When invoking a native command, a call like & $program $programargs will correctly escape the array of arguments so that it is parsed correctly by the executable. However, for a PowerShell cmdlet, script, or function, there is no external programming requiring a serialize/parse round-trip, so the array is passed as-is as a single value.
Instead, you can use splatting to pass the elements of an array (or hashtable) to a script:
& $scriptfile #scriptargs
The # in & $scriptfile #scriptargs causes the values in $scriptargs to be applied to the parameters of the script.
You're passing the variables as a single object, you need ot pass them independently.
This here works:
$scriptfile = ".\callee.ps1"
& $scriptfile a b c
So does this:
$scriptfile = ".\callee.ps1"
$scriptargs = #(
"a",
"b",
"c"
)
& $scriptfile $scriptargs[0] $scriptargs[1] $scriptargs[2]
If you need to pass it as a single object, like an array, then you can have the callee script split it; the specific code for that would depend on the type of data you're passing.
Use Invoke-Expression cmdlet:
Invoke-Expression ".\callee.ps1 $scriptargs"
As the result you'll get :
PS > Invoke-Expression ".\callee.ps1 $scriptargs"
Parameter one: a
Parameter two: b
Parameter three: c
PS >
I have a script internal.ps1 which accepts certain params:
param ($paramA, $paramB)
Write-Host $PSBoundParameters
And a script caller.ps1 that calls it:
.\internal -paramA A -paramB B
It works great:
PS C:\temp> .\caller
[paramA, A] [paramB, B] <<<< bounded to both params
However, in caller I want to keep the parameters to internal in a var, and use it later. However, that doesn't work:
$parms = "-paramA A -paramB B"
# Later...
.\internal $parms
Result: [paramA, A -paramB B] <<<<< All got bounded to ParamA
Neither does using an array:
$parms = #("A", "B")
# Later...
.\internal $parms
Result: [paramA, System.Object[]] <<<< Again, all bound to ParamA
How can I accomplish this? Note that the actual commandline is more complex, and may have unknown length.
The splatting operator (#) should do what you need.
Consider first this simple function:
function foo($a, $b) { "===> $a + $b" }
Calling with explicit arguments yields what you would expect:
foo "hello" "world"
===> hello + world
Now put those two values in an array; passing the normal array yields incorrect results, as you have observed:
$myParams = "hello", "world"
foo $myParams
===> hello world +
But splat the array instead and you get the desired result:
foo #myParams
===> hello + world
This works for scripts as well as for functions. Going back to your script, here is the result:
.\internal #myParams
[paramA, hello] [paramB, world]
Finally, this will work for an arbitrary number of parameters, so know a priori knowledge of them is needed.
powershell -file c:\temp\test.ps1 #("A","B")
or
powershell -command "c:\temp\test.ps1" A,B
Your script expects 2 arguments, but your previous attempts pass just a single one (a string and an array respectively). Do it like this:
$parms = "A", "B"
#...
.\internal.ps1 $parm[0] $parm[1]
I'm attempting to pass a property to MSBuild. The property is a semicolon-delimited list of values. Unlike this question, I'm running MSBuild from PowerShell.
I get:
PS> msbuild .\Foo.sln /p:PackageSources="\\server\NuGet;E:\NuGet"
MSBUILD : error MSB1006: Property is not valid.
Switch: E:\NuGet
If I run the same command from Command Prompt, it works fine. How do I get it to work in PowerShell?
Wrap the parameter in single quotes:
... '/p:PackageSources="\\Server\NuGet;E:\NuGet"'
On PowerShell v3 try this:
msbuild .\Foo.sln --% /p:PackageSources="\\Server\NuGet;E:\NuGet"
Also using ASCIII value helps:
msbuild .\Foo.sln /p:PackageSources="\\Server\NuGet%3BE:\NuGet"
VBScript function below can be used to escape property values passed to MSBuild.exe inside double quotes:
Function Escape(s)
Escape = s
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = True
objRegEx.Pattern = "(\\+)?"""
Escape = objRegEx.Replace(Escape,"$1$1\""")
objRegEx.Pattern = "(\\+)$"
Escape = objRegEx.Replace(Escape,"$1$1")
End Function
The following example demonstrates usage of Escape() function
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "msbuild.exe echo.targets /p:Param1=""" & Escape("ParamValue1") & """,Param2=""" & Escape("ParamValue1") & """", 1, True