Arguments in Powershell Script [duplicate] - powershell

This question already has an answer here:
dotnet ef scaffold Unrecognized option '-t firstTable -t secondTable' - pass arguments stored in a string
(1 answer)
Closed yesterday.
I try to manipulate pictures with imageick. On the CLI I use this working fine command:
C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64\magick.exe D:\orG\3.08_10-02_1.jpg -resize 1200x1200 D:\orG\output\3.08_10-02_1.jpg
But I try to that in a powershell script (test.ps1) with this code:
$CMD = 'C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64\magick.exe'
$arg2 = 'D:\orG\3.08_10-02_1.jpg'
$arg3 = '-resize 1200x1200'
$arg4 = 'D:\orG\output\3.08_10-02_1.jpg'
& $CMD $arg2 $arg3 $arg4
Im getting this error: magick.exe: unrecognized option `-resize 1200x1200' at CLI arg 2 # fatal/magick-cli.c/ProcessCommandOptions/659.
I have also tried:
$CMD = 'C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64\magick.exe'
$arg1 = 'convert'
$arg2 = 'D:\orG\3.08_10-02_1.jpg'
$arg3 = '-resize 1200x1200'
$arg4 = 'D:\orG\output\3.08_10-02_1.jpg'
& $CMD $arg1 $arg2 $arg3 $arg4
But the error is similar: convert: unrecognized option `-resize 1200x1200' # error/convert.c/ConvertImageCommand/2686.
If I remove the option "-resize 1200x1200". There is now error message and the command is writing the file (without resize) to the output folder. So I think, that I have a problem with my option, but I can't find it. I have invested several hours and als tried with the #-symbol for arguments, without success.

You have to separate argument name and value, otherwise -resize 1200x1200 will be passed as a single token, but native executables usually expect argument name and value to be separate tokens (unless the syntax is without whitespace, e. g. -name:value).
$CMD = 'C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64\magick.exe'
$arg1 = 'convert'
$arg2 = 'D:\orG\3.08_10-02_1.jpg'
$arg3 = '-resize', '1200x1200'
# |<-- inserted comma here
$arg4 = 'D:\orG\output\3.08_10-02_1.jpg'
& $CMD $arg1 $arg2 $arg3 $arg4
This turns $arg3 into an array of two strings, which PowerShell passes as separate argument tokens. This is also called splatting and is only done when calling native executables (for PowerShell commands you have to use the # splatting operator).
Another, more concise way:
$CMD = 'C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64\magick.exe'
# Create an array of arguments
$imArgs = #(
'convert'
'D:\orG\3.08_10-02_1.jpg'
'-resize', '1200x1200'
'D:\orG\output\3.08_10-02_1.jpg'
)
# Call native command, which splats argument array.
& $CMD $imArgs
# Alternatively you can be explicit about splatting:
& $CMD #imArgs
Make sure you don't name the variable $args which is a reserved PowerShell variable.
As an aside, if you want to be able to call ImageMagick without having to hardcode its path in all of your scripts, add the installation directory of ImageMagick ("C:\ProgrammeNoSetup\ImageMagick-7.1.0-62-portable-Q16-HDRI-x64") to the PATH environment variable.
You could then call ImageMagick simply by specifying the name of its executable:
magick #imArgs

Related

NUnit access denied error in dotcover.exe

I am writing PowerShell script for dotCover to generate coverage report using NUnit-console.exe
After I run the script -
$testRunner="C:\Program Files (x86)\NUnit 2.6.2\bin\nunit-console.exe"
$testContainers="path/to/test1.dll","path/to/test2.dll"
$dotcover="D:\JetBrains.dotCover.CommandLineTools.2019.3.4\dotcover.exe"
foreach($test in $testContainers)
{
$testAssembly=Get-Item $test
$testName= $testAssembly.BaseName
&$dotcover cover /TargetExecutable=$testRunner /TargetArguments=$test /Output="D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\$testName.dcvr"
}
$testReports=$testContainer|%{
$testAssembly=Get-Item $test
$name= $testAssembly.BaseName
return ("D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\{0}.dcvr" -f $name)
}
$testReportArguments=[String]::Join(";",$testReports)
&$dotcover merge /Source="$testReportArguments" /Output="D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.dcvr" /ReportType="DCVR"
&$dotcover report /Source="D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.dcvr" /Output="D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.html" /ReportType="HTML"
It is giving the following error-
Unhandled Exception:
System.UnauthorizedAccessException: Access to the path 'C:\Program Files (x86)\NUnit 2.6.2\bin\TestResult.xml' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String
msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
at System.IO.StreamWriter..ctor(String path)
at NUnit.ConsoleRunner.ConsoleUi.Execute(ConsoleOptions options)
at NUnit.ConsoleRunner.Runner.Main(String[] args)
Even though that TestResult.xml file is not at that location it is at-
C:\Program Files (x86)\NUnit 2.6.2\doc\files\TestResult.xml
but I copied that file and put it to the bin folder but still the error persists.
Is this problem related to rights or something? any way to get rid of this?
and after this the execution is at halt neither failing nor passing.
Anytime you see 'Access Denied', then yes, it's a permissions / ACL problem. So, you do need to check that.
Honestly, I'd suggest adding those 'exe' paths to your Windows System Path or the PowerShell environment path. However, taking what you've shown thus far, as per the link I included in my comment:
PowerShell: Running Executables
The Call Operator &
Why:
Used to treat a string as a SINGLE command. Useful for dealing with
spaces. In PowerShell V2.0, if you are running 7z.exe (7-Zip.exe) or
another command that starts with a number, you have to use the
command invocation operator &. The PowerShell V3.0 parser do it now
smarter, in this case you don’t need the & anymore.
Details:
Runs a command, script, or script block. The call operator, also known
as the "invocation operator," lets you run commands that are stored in
variables and represented by strings. Because the call operator does
not parse the command, it cannot interpret command parameters
# Example:
& 'C:\Program Files\Windows Media Player\wmplayer.exe' "c:\videos\my home video.avi" /fullscreen
Things can get tricky when an external command has a lot of parameters
or there are spaces in the arguments or paths!
With spaces you have to nest Quotation marks and the result it is not
always clear!
In this case it is better to separate everything like so:
$CMD = 'SuperApp.exe'
$arg1 = 'filename1'
$arg2 = '-someswitch'
$arg3 = 'C:\documents and settings\user\desktop\some other file.txt'
$arg4 = '-yetanotherswitch'
& $CMD $arg1 $arg2 $arg3 $arg4
# or same like that:
$AllArgs = #('filename1', '-someswitch', 'C:\documents and settings\user\desktop\some other file.txt', '-yetanotherswitch')
& 'SuperApp.exe' $AllArgs
So, this change should get you going. Again, I don't use the tool, so no way to validate and thus you may need to tweak.
See also:
Implementing DotCover from Powershell
$testRunner = 'C:\Program Files (x86)\NUnit 2.6.2\bin\nunit-console.exe'
$testContainers = 'path/to/test1.dll','path/to/test2.dll'
$dotcover = 'D:\JetBrains.dotCover.CommandLineTools.2019.3.4\dotcover.exe'
foreach($test in $testContainers)
{
$testAssembly = Get-Item $test
$testName = $testAssembly.BaseName
$arg1 = 'cover'
$arg2 = '/TargetExecutable = $testRunner'
$arg3 = '/TargetArguments = $test'
$arg4 = '/TargetExecutable = $testRunner'
$arg5 = '/Output = "D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\$testName.dcvr"'
& $dotcover $arg1 $arg2 $arg3 $arg4 $arg5
}
$testReports = $testContainer |
%{
$testAssembly = Get-Item $test
$name = $testAssembly.BaseName
return ('D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\{0}.dcvr' -f $name)
}
$testReportArguments = [String]::Join(';',$testReports)
$arg1 = 'merge'
$arg2 = '/Source = $testReportArguments'
$arg3 = '/Output = "D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.dcvr"'
$arg4 = '/ReportType = "DCVR"'
& $dotcover $arg1 $arg2 $arg3 $arg4
$arg1 = 'report'
$arg2 = '/Source = "D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.dcvr"'
$arg3 = '/Output = "D:\JetBrains.dotCover.CommandLineTools.2019.3.4\TestReport\mergedReport.html"'
$arg4 = '/ReportType="HTML"'
& $dotcover $arg1 $arg2 $arg3 $arg4

Executing Powershell script from command line with quoted parameters

I am automating the build of a legacy MS Access application, and in one of the steps, I am trying to make an Access executable (.ADE). I have come up with the following code, which is stored in a file (PSLibrary.ps1):
Add-Type -AssemblyName Microsoft.Office.Interop.Access
function Access-Compile {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
Write-Output "Starting MS Access"
$access = New-Object -ComObject Access.Application
$access.Visible = $FALSE
$access.AutomationSecurity = 1
if (!(Test-Path $source)) { Throw "Source '$source' not found" }
if ((Test-Path $destination)) {
Write-Output "File '$destination' already exists - deleting..."
Remove-Item $destination
}
Write-Output "Compiling '$source' to '$destination'"
$result = $access.SysCmd(603, $source, $destination)
$result
Write-Output "Exiting MS Access"
$access.quit()
}
If I go into the PowerShell ISE and run the command below, then everything works fine, and the expected output is created:
PS C:>& "C:\Temp\PSLibrary.ps1"
PS C:>Access-Compile "C:\Working\Project.adp" "C:\Working\Project.ade"
However, I can't seem to generate the right hocus-pocus to get this running from the command line, as I would in an automated build. For instance,
powershell.exe -command "& \"C:\\Temp\\PSLibrary.ps1\" Access-Compile \"C:\\Temp\\Project.adp\" \"C:\\Temp\\Project.ade\""
What am I doing wrong?
For complex parameters, you can use Powershell's -EncodedCommand parameter. It will accept a Base64 encoded string. No escaping is needed for quotes, slashes and such.
Consider a test function that will print its parameters. Like so,
function Test-Function {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
write-host "src: $source"
write-host "dst: $destination"
}
Create command to load the script and some parameters. Like so,
# Load the script and call function with some parameters
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
After the command syntax is OK, encode it into Base64 form. Like so,
[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes('. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""'))
You'll get a Base64 string. Like so,
LgAgAEMAOgBcAFQAZQBtAHAAXABDAGEAbABsAGkAbgBnAC0AVABlAHMAdAAuAHAAcwAxADsAIAAgAFQAZQBzAHQALQBGAHUAbgBjAHQAaQBvAG4AIAAiAHMAbwBtAGUAXABzAHAAZQBjAGkAYQBsADoAYwBoAGEAcgBhAGMAdABlAHIAcwA/ACIAIAAiAGAAIgBjADoAXABtAHkAIABwAGEAdABoAFwAdwBpAHQAaABcAHMAcABhAGMAZQBzACAAdwBpAHQAaABpAG4ALgBlAHgAdABgACIAIgA=
Finally, start Powershell and pass the encoded string as a parameter. Like so,
# The parameter string here is abreviated for readability purposes.
# Don't do this in production
C:\>powershell -encodedcommand LgAgA...
Output
src: some\special:characters?
dst: "c:\my path\with\spaces within.ext"
Should you later want to reverse the Base64 encoding, pass it into decoding method. Like so,
$str = " LgAgA..."
[Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($str))
# Output
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
PowerShell like Bash can take single or double quotes
PS C:\Users\Steven> echo "hello"
hello
PS C:\Users\Steven> echo 'hello'
hello
this can alleviate some of the headache, also I think you can use the literal backslashes without escaping.
To run PowerShell, choose
Start Menu Programs Accessories
Windows Powershell Windows Powershell

Strange order of variables written from Write-Host within function

With my following code seems Write-Host puts out variables in a strange way (for me coming from C# at least).
Code is here
function Run(
[string] $command,
[string] $args
)
{
Write-Host 'from function - command is:' $command '.args is: ' $args
}
$cmd = "ping"
$args = "208.67.222.222"
Write-Host 'from main - command is:' $cmd '.args is: ' $args
Run("ping","208.67.222.222")
Output is here
from main - command is: ping .args is: 208.67.222.222
from function - command is: ping 208.67.222.222 .args is:
How come Write-Host from main works as I expect, but within the function it outputs all variables at the same time? How can I correct this behaviour?
$args in the function is an automatic variable. It is an array that contains all the arguments passed to the function.
Use something besides $args for your IP address variable.

Executing an exe with arguments using Powershell

This is what I want to execute:
c:\Program Files (x86)\SEQUEL ViewPoint\viewpoint.exe /Setvar((POSTSTR $POSTSTR)(POSTEND $POSTEND)) /G:C:\viewpointfile.vpt /D:C:($BEGDATE to $TODDATE).xls
This is what I have tried:
$a = "/Setvar((POSTSTR $POSTSTR)(POSTEND $POSTEND))"
$b = "/G:C:\viewpointfile.vpt"
$c = "/D:C:($BEGDATE to $TODDATE).xls"
$Viewpoint = "c:\Program Files (x86)\SEQUEL ViewPoint\viewpoint.exe"
&$Viewpoint $a $b $c
When I execute this I receive an error stating:
File C:\viewpointfile.vpt "/D:C:($BEGDATE to $TODDATE).xls" not found!
I'm not sure where it gets the extra quotes from. If I run the command with just $a and $b it runs fine.
Any help would be greatly appreciated. Thanks! :)
Update
manojlds suggested echoargs so here it the output from it:
&./echoargs.exe $viewpoint $a $b $c
Arg 0 is C:\Program Files (x86)\SEQUEL ViewPoint\viewpoint.exe
Arg 1 is /Setvar((POSTSTR 20101123)(POSTEND 20111123))
Arg 2 is /G:C:\viewpointfile.vpt
Arg 3 is /D:C:(2010-11-23 to 2011-11-23 PM).xls
It appears that all the arguments are being passed properly. When I run this as a command in cmd.exe it executes perfectly. So something on Powershells end must be messing up the output.
Is there any other way to go about executing this command using Powershell?
I've found the method blogged by Joel Bennett to be the most reliable when calling legacy commands
http://huddledmasses.org/the-problem-with-calling-legacy-or-native-apps-from-powershell/
I've had to use this when calling LogParser from Powershell:
set-alias logparser "C:\Program Files (x86)\Log Parser 2.2\LogParser.exe"
start-process -NoNewWindow -FilePath logparser -ArgumentList #"
"SELECT * INTO diskspaceLP FROM C:\Users\Public\diskspace.csv" -i:CSV -o:SQL -server:"Win7boot\sql1" -database:hsg -driver:"SQL Server" -createTable:ON
"#
Get echoargs.exe from Powershell community extension ( http://pscx.codeplex.com/ ) to figure out the arguments that Powershell sends to your exe.
$a = "/Setvar((POSTSTR $POSTSTR)(POSTEND $POSTEND))"
$b = "/G:C:\viewpointfile.vpt"
$c = "/D:C:($BEGDATE to $TODDATE).xls"
$echoArgs = ".\echoargs.exe"
&$echoArgs $a $b $c
You seem to be passing the arguments fine however, but the viewpoint.exe seems to be acting up. I don't see what you are doing here:
$c = "/D:C:($BEGDATE to $TODDATE).xls"
After C: there is no \ and also your error message that you have pasted shows $BEGDATE and $TODDATE verbatim, which is not possible as they would have been substituted with their values.
If I can't run a command like this it usually works for me with Invoke-Expression. Can't test yours though.
Invoke-Expression "$viewpoint $a $b $c"

Passing around command line $args in powershell , from function to function

This is a nasty issue I am facing. Wont be surprised if it has a simple solution, just that its eluding me.
I have 2 batch files which I have to convert to powershell scripts.
file1.bat
---------
echo %1
echo %2
echo %3
file2.bat %*
file2.bat
--------
echo %1
echo %2
echo %3
On command line, I invoke this as
C:> file1.bat one two three
The output I see is as expected
one
two
three
one
two
three
(This is a crude code sample)
When I convert to Powershell, I have
file1.ps1
---------
Write-Host "args[0] " $args[0]
Write-Host "args[1] " $args[1]
Write-Host "args[2] " $args[2]
. ./file2.ps1 $args
file2.ps1
---------
Write-Host "args[0] " $args[0]
Write-Host "args[1] " $args[1]
Write-Host "args[2] " $args[2]
When I invoke this on powershell command line, I get
$> & file1.ps1 one two three
args[0] one
args[1] two
args[2] three
args[0] one two three
args[1]
args[2]
I understand this is because $args used in file1.ps is a System.Object[] instead of 3 strings.
I need a way to pass the $args received by file1.ps1 to file2.ps1, much the same way that is achieved by %* in .bat files.
I am afraid, the existing manner will break even if its a cross-function call, just the way its a cross-file call in my example.
Have tried a few combinations, but nothing works.
Kindly help. Would much appreciate it.
In PowerShell V2, it's trivial with splatting. bar just becomes:
function bar { foo #args }
Splatting will treat the array members as individual arguments instead of passing it as a single array argument.
In PowerShell V1 it is complicated, there's a way to do it for positional arguments. Given a function foo:
function foo { write-host args0 $args[0] args1 $args[1] args2 $args[2] }
Now call it from bar using the Invoke() method on the scriptblock of the foo function
function bar { $OFS=','; "bar args: $args"; $function:foo.Invoke($args) }
Which looks like
PS (STA) (16) > bar 1 2 3
bar args: 1,2,3
args0 1 args1 2 args2 3
when you use it.
# use the pipe, Luke!
file1.ps1
---------
$args | write-host
$args | .\file2.ps1
file2.ps1
---------
process { write-host $_ }