Running an executable whose parameter values are specified as variables in powershell - powershell

I am writing a release script in powershell and need to invoke a custom executable and pass in a bunch of parameters to the executable. Few of these parameters are assigned values from various conditions and executions in the script. I see that the values are not getting evaluated when the executable is invoked. Some of the variables need to be passed in as a series of arguments inside double quotes as well. Shown below is a smaple call
mysample.exe '-forcerebuild' '-release=$CALC_VERSION' '-projfile=sample.proj' '-buildoptions="/p:AllowDevDependencies=true /p:AssemblyVersion=$CALC_ASSEMBLY_VERSION /flp:LogFile=$env:temp\build.log /p:BaseIntermediateOutputPath=$PROJECT_BASE\build\ /clp:nosummary"'
All the variables that need to be evaluated are highlighted. The exe expects all the options provided to -buildoptions inside quoted -buildoptions="all build options"
The values don't seem to get evaluated. However, if i put build options as a separate variable and print it, the values seem to get evaluated. I am new to powershell and would really appreciate any help with this.

Note that powershell doesn't do variable expansion inside single quoted strings. For example:
$test_variable = "test"
write-host "this is a $test_variable"
write-host 'this is a $test_variable'
will print
this is a test
this is a $test_variable
Also, powershell has problems in passing arguments to exes. There is a util called EchoArgs.exe that comes with Powershell Community Extensions. Use that and pass your argument to that to see if they are passed fine.

Related

Is it possible to retrieve an #argument in a Powershell shell from within a program?

I am writing a program prog.exe that retrieves all arguments that are passed to it (in the form of a "sentence", not standalone arguments).
I just realized that in some cases only part of the line is retrieved, and this is when there are #parameters:
PS > ./prog.exe this is a #nice sentence
Only this, is and a are retrieved. In case I do not use # I get all of them. I presume this is because everything after the # is interpreted by Powershell as a comment.
Is there a way to retrieve everything that is on the command line?
If this makes a difference, I code in Go and get the arguments via os.Args[1:].
You can prevent PowerShell from interpreting # as a comment token by explicitly quoting the input arguments:
./prog.exe one two three '#four' five
A better way exists, though, especially if you don't control the input: split the arguments into individual strings then use the splatting operator # on the array containing them:
$paramArgs = -split 'one two three #four five'
./prog.exe #paramArgs
Finally, using the --% end-of-parsing token in a command context will cause the subsequent arguments on the same line to be passed as-is, no parsing of language syntax:
./prog.exe --% one two three #four five

Always append arguments to command in Powershell

There's a few commands I need to run repetitively in Powershell, with some variable arguments in content and length, but some arguments must always be there. I don't want to forget those arguments, so is there a way to create a function that does this?
I have tried using things like Invoke-Expression, but when I use brackets in a command, Powershell thinks it's a type and tells me my cast is not valid. If I escape the argument list and provide an argument in the form Key=Value, PowerShell parses it into a System.Object[] and the command fails. I figured it might be better not to ask about how to fix that, but how to solve my root problem.
You can consider this a somewhat duplicate of How to always append an ampersand for certain commands? (MacOS / bash) but for PowerShell.
Just write your own version of the command as a Function with mandatory parameters:
Function RunMyCommand {
Param(
[Parameter(Position=0,mandatory=$true)][string]$Argument1,
[Parameter(Position=1,mandatory=$true)][int]$Argument2,
[Parameter(Position=2,mandatory=$true)]$Argument3
)
# Amend this according to the syntax and string manipulation required for your command
ThisIsMyCommand $Argument1 $Argument2 $Argument3
}
RunMyCommand -Argument1 ThisString -Argument2 ThisNumber -Argument3 ThisAnything

Powershell function call changing passed string into int

So I am using the kind of buggy Sapien powershell studio to make a powershell driven GUI application, and I am attempting to perform an ADSI query.
$nameOfDeviceInput is a System.Windows.Forms.TextBox
On one form, I have the following function:
$buttonPerformAction_Click={
if (FindInAD($nameOfDeviceInput.Text).Count -gt 0)
{
$buttonPerformAction.BackColor = 'Red'
$buttonPerformAction.Text = "System already exists in AD with that name. Try another name"
return
}
.....
}
On the "main" form, I have the function FindInAD
function FindInAd($nameOfSystem)
{
Write-Host "seeking system" $nameOfSystem
([adsisearcher]"(CN=$nameOfSystem)").FindAll()
}
FindInAd() is failing because for whatever reason, $nameOfSystem is set to 1, and if I don't explicitly cast it as a string, it gets implicitly cast to Int32 (obviously)
I have tried the following:
Fully qualifying the textbox input by notating the form it belongs to ( $adObjectModifier )
$buttonPerformAction_Click={
if (FindInAD($adObjectModifier.$nameOfDeviceInput.Text).Count -gt 0)
{
$buttonPerformAction.BackColor = 'Red'
$buttonPerformAction.Text = "System already exists in AD with that name. Try another name"
return
}
.....
}
Explicitly casting the $nameOfSystem parameter as a type of [string]
function FindInAd([string]$nameOfSystem)
{
Write-Host "seeking system" $nameOfSystem
([adsisearcher]"(CN=$nameOfSystem)").FindAll()
}
Passing a raw string into FindInAD from the AdObjectModifier form.
....
if (FindInAD("Test").Count -gt 0)
....
There is nothing else on the output pipeline at the time, (at least not from me) in between the method invocation. It is EventHandler > Function Call with String parameter
Why are the strings I'm passing getting changed to a digit???
EDIT: I think my passed parameter is being automatically replaced with the resulting boolean somehow, but this doesn't make any sense to me....
Your have a syntax problem:
FindInAD($nameOfDeviceInput.Text).Count # WRONG
Note: Wrong in this context means: the syntax is formally valid, but doesn't do what you expect - see the bottom section.
It should be:
(FindInAD $nameOfDeviceInput.Text).Count
PowerShell commands - functions, cmdlets, scripts and external programs - are invoked like shell commands - foo arg1 arg2 - and not like C# methods - foo('arg1', 'arg2').
That is:
Do not put (...) around the list of arguments.
However, you do need (...) around the call as a whole if you want a command call to participate in an expression, as shown above with the access to property .Count - see this answer for more information.
Separate arguments with spaces, both from each other and from the command name - do not use ,
, between arguments functions differently: It constructs an array that is passed as a single argument - see below.
You may pass simple strings (ones that contain neither spaces nor PowerShell metacharacters such as ; or &) as barewords; that is, quoting them is optional; e.g., instead of foo 'bar', you can call foo bar - see this answer for how PowerShell parses unquoted command arguments.
Also, if a target function or script has explicitly declared parameters (which binary cmdlets invariably do), such as -bar and -baz, you can pass your values as named arguments, i.e. by prepending them with the target parameter name; doing so is good practice in scripts: foo -bar arg1 -baz arg2
By contrast, calling methods of objects uses the syntax familiar from regular programming languages such as C# ($obj.foo('arg1', 'arg2'))
This difference relates two PowerShell's two fundamental parsing modes, explained in detail in this answer:
Commands are parsed in argument mode - as in shells.
Method calls and operator-based expressions are parsed in expression mode - as in regular programming languages.
These modes are required in order to allow PowerShell serve double duty: as a shell on the one hand, and as a scripting (programming) language on the other.
PowerShell can help you avoid this syntax problem:
Note that the problem isn't that using method syntax to call a command is invalid syntax, but that it doesn't work as intended, which can be difficult to diagnose.
In short: When you call command foo as foo('foo', 'bar'), ('foo', 'bar')is a 2-element array, which is then passed to foo as a single argument.
To prevent the problem to begin with, you can set Set-StrictMode to -Version 2 or higher, which makes PowerShell report an error if you accidentally use method syntax when calling a command:
# Turn on the check for accidental method syntax.
# Note: This also turns on ADDITIONAL checks - see below.
Set-StrictMode -Version 2
# This call now produces an ERROR, because the proper syntax would be:
# foo 'a' 'b'
foo('a', 'b')
Caveats:
Set-StrictMode -Version 2 comprises additional strictness checks that you must then also conform to, notably:
You must not reference non-existent variables.
You must not reference non-existent properties; see GitHub issue #2798 for an associated pitfall in connection with PowerShell's unified handling of scalars and collections.
An error is reported only for pseudo method calls with multiple arguments (e.g.,
foo('bar', 'baz')), not with only one; e.g., foo('bar') is accepted, because the single-argument case generally still (accidentally) works.
The errors reported for strictness violations are statement-terminating errors: that is, they only terminate the statement at hand, but by default the script continues; to ensure that overall execution aborts - on any type of error - you'd have to set
$ErrorActionPreference = 'Stop' at the start of your code. See this answer for more information.
As for what you tried:
FindInAD($nameOfDeviceInput.Text).Count
is the same as:
FindInAD ($nameOfDeviceInput.Text).Count
That is, the result of expression ($nameOfDeviceInput.Text).Count is passed as an argument to function FindInAD.

C-like macro expansion in PowerShell

Recently I've started fiddling around with PowerShell, and I bumped into an issue with running .jar files. Simply put, I'm using plantuml and I'd like to simply have a "plantuml" command to run it. Normally, running the program would be done by typing. java -jar C:\Users\Name\Documents\WindowsPowerShell\Commands\plantuml.jar. This is of course quite a handful, and I'd like to shorten this to simply plantuml.
My current work-around is the following function:
function plantuml($UmlPath, $ImgPath) {
java -jar C:\Users\Name\Documents\WindowsPowerShell\Commands\plantuml.jar $UmlPath $ImgPath
}
However, I cannot pass any parameters to the .jar file like this, because Powershell intercepts them and interprets them as function parameters. A current workaround for this is by wrapping them in quotation marks, but I find this ugly and I often forget.
Is there any way to simply be able to type plantuml so that PowerShell expands it to java -jar C:\Users\Name\Documents\WindowsPowerShell\Commands\plantuml.jar? The only similar question I found was this one, but it doesn't appear to have an actual answer.
I don't have plantuml or anything similar to test with, but you can get all the parameters passed to a function with the $args variable, so this approach might work:
function plantuml {
# Array of your default arguments to Java.exe to start plantuml
$arguments = #('-jar',
'C:\Users\Name\Documents\WindowsPowerShell\Commands\plantuml.jar')
# All arguments passed to this function, umlpath, imgpath, and anything else
# are in the special variable $args, add those into the array as well.
$arguments += $args
Start-Process Java -ArgumentList $arguments
}

Call an executable with quotes at specific positions

I want to call an executable from a PowerShell script that requires quotes at specific positions in the argument list. Although I found similar questions I did not find a solution at all.
This is what the command must look like on the command line:
reptool.exe --profile="C:\My profile"
The parameter value ("C:\Profiles...") is supposed to be generated dynamically using a variable:
$repToolProfile = "C:\My profile"
This is what I have already tried:
&"reptool.exe" --profile=$repToolProfile
Fails as the argument is given as "--profile=C:\My profile" (quotes around the whole argument).
&"reptool.exe" --profile="$repToolProfile"
Fails as the argument is given as "--profile=C:\My profile" (quotes around the whole argument, same as above).
&"reptool.exe" "--profile=`"$repToolProfile`"
Fails as the argument is given as "--profile="C:\My profile"" (quotes around the whole argument and the value).
I cannot use single quotes or the "verbatim operator" (--%) as I have to use a PowerShell variable, neither I can use Start-Process as it is called asynchroneously (even when I use the -Wait parameter. Also I want to check the exit code. I don't want to convert my arguments to Base64.
This worked for me:
$command = '& "reptool.exe" --% ' + "--profile=`"$repToolProfile`"
Invoke-Expression $command