I use powershell as shell in Windows. When I'm trying to launch some application who's dll dependencies are missing in PATH environment variable, then nothing happens, powershell just silently returns with new command prompt.
Is there a way to make powershell fail louder, telling me what exactly is missing, like default cmd shell does?
I was having this same problem. PowerShell was setting $LASTEXITCODE code to -1073741515 (0xC0000142, 3221225794) but no output explaining what was actually wrong. When running it via cmd.exe I would get popup with something like:
The code execution cannot proceed because some.dll was not found. Reinstalling the program may fix this problem.
cygwin bash outputs errors relating to dll not found to stderr and if you run the the same via bash from PowerShell then you can see the error:
> & 'C:\tools\cygwin\bin\bash.exe' '-c' '"C:/Users/xxx/dir/main.exe"'
C:/Users/xxx/dir/main.exe: error while loading shared libraries: another.dll: cannot open shared object file: No such file or directory
This works with git bash also:
> & 'C:\Program Files\Git\bin\bash.exe' '-c' '"C:/Users/xxx/dir/main.exe"'
C:/Users/xxx/dir/main.exe: error while loading shared libraries: another.dll: cannot open shared object file: No such file or directory
Quite a hack but better than nothing.
You could echo the %ERROR% variable, which stores errors until the PowerShell window is closed.
Update: In PowerShell, you could use the Get-Error command, or look at the $Error variable.
Another way would be to use Dependancy walker, if you can use a command line option, then you should be able to use this in PowerShell.
I am afraid there is no way to get that info... But try to read
An Introduction to Error Handling in PowerShell http://blogs.msdn.com/b/kebab/archive/2013/06/09/an-introduction-to-error-handling-in-powershell.aspx
or
PowerShell Tutorial – Try Catch Finally and error handling in PowerShell
http://www.vexasoft.com/blogs/powershell/7255220-powershell-tutorial-try-catch-finally-and-error-handling-in-powershell
Try
{
$AuthorizedUsers = Get-Content \\ FileServer\HRShare\UserList.txt -ErrorAction Stop
}
Catch [System.OutOfMemoryException]
{
Restart-Computer localhost
}
Catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Send-MailMessage -From ExpensesBot#MyCompany.Com -To WinAdmin#MyCompany.Com -Subject "HR File Read Failed!" -SmtpServer EXCH01.AD.MyCompany.Com -Body "We failed to read file $FailedItem. The error message was $ErrorMessage"
Break
}
Finally
{
$Time=Get-Date
"This script made a read attempt at $Time" | out-file c:\logs\ExpensesScript.log -append
}
Related
I essentially require a functionality in Powershell that executes the given string (it can be a CMD/Powershell command, a perl/python/powershell with arguments or an exe with arguments, etc) captures its exit value.
In perl I would pass the string to 'system()' and use the '$CHILD_ERROR' perlval and shift it to access the exit code.
In powershell I am clueless.
I tried using Invoke-Expression, but even if the expression passed to Invoke-Expression fails, the Invoke-Expression call itself will have succeeded.
You can use $LASTEXITCODE to get the exit code from an external program or the Boolean $? to check if the last operation succeeded or failed. Run Get-Help about_Automatic_Variables -ShowWindow from a PowerShell console to see more details.
You may want to check out the & (call) command as an alternative to Invoke-Expression when running external programs. Run Get-Help about_Automatic_Variables -ShowWindow from a PowerShell console for details.
Also remember you may be able to just call the external program without using one of the commands above. See the example below:
param($Hostname="127.0.0.1", $Tries=1, $Wait=1000)
$output = ping.exe $Hostname -n $Tries -w $Wait # captures anything written to stdout
$output|? {$_ -match 'Request timed out'}|Write-Warning
$LASTEXITCODE # returns the exit code from ping.exe
You can copy it to a test.ps1 file and run it from a PowerShell console window (.\test.ps1 8.8.8.8 for instance) to see how it works.
Current PS script:
Invoke-Command -ComputerName RemoteServer007.FQDN.com -ScriptBlock {
Set-Variable -Name WOWCONFIG -value "d:\ABCs\WOWzers" `
| Start-Process "d:\da-folder\Do-It-NOW-Pleez.cmd"
}
If I log on locally to the server(RemoteServer007.FQDN.com) and execute the cmd file, it runs through all of the lines(commands) within the cmd file.
When I execute it remotely, it gets about 30% of the way through the commands within the cmd file, the PS execution ends without error, but not all of the lines/commands in the cmd file had been executed.
This was discovered by simply configuring each line of the cmd file to output to txt files.
I even tried re-ranging the commands in the cmd file, thinking that perhaps there was a specific command that was causing it to exit, but that is not the case.
I'm wondering if there is some timeout or response that PowerShell is not getting? and just quitting almost immediately after starting?
Any ideas or help would be greatly appreciated.
There are a couple of things you can do here:
You may have a memory issue. Increasing the value of MaxMemoryPerShellMB might help
set-item WSMan:\$target\Shell\MaxMemoryPerShellMB -Value 0 -Force
You'd need to run this once on the remote machine before you execute your commands again.
You can also see possible error logs in the windows event viewer. There are categories for powershell and for Windows Remote Management which you should look at.
Finally, you can just run this process asynchronously, using the task scheduler for instance. I had a similar problem with windows in the past, and running the process from the task scheduler, outside the powershell session, fixed it. There's an example of how we did this in Cloudify here:
https://github.com/CloudifySource/cloudify/blob/master/esc/src/main/resources/clouds/ec2-win/upload/bootstrap-management.ps1#L220
I seem to have run into a problem/bug when trying to capture tracing output when running a Powershell script from Control-M.
The output file shows the headers and footers of the start-trace and stop-trace commands, but it does not show anything else I try to capture. Specifically, if my script issues a write-host command somewhere, then that information is not captured in the output (trace) file.
Here is a super simple script that illustraes my problem:
start-transcript -path "C:\Powershell\transcript.log"
write-host "test message"
#do stuff...
stop-transcript
Here is an example of my current output when running the script through Control-M:
**********************
Windows PowerShell Transcript Start
Start time: 20140212002005
Username : mydomain\SYSTEM
Machine : myserver (Microsoft Windows NT 6.1.7601 Service Pack 1)
**********************
**********************
Windows PowerShell Transcript End
End time: 20140212002008
**********************
Note that my test message does not show up! This only happens when I run the script via Control-M. When I run my script manually, my "test message" does show up in the transcript output.
My first suspicion was file permissions, but those look good to me. The Control-M agent uses system level access, so it should have all the permissions it needs anyway. If it were a file permission issue, I don't believe i would even get the header/footer messages.
I'm on PS v2.0. My server is running 2008r2.
Any thoughts appreciated...
Write-Host writes to to the console window, which is not what's being "watched" by Control-M. Try Write-Output instead. Write-Host is usually not what you want for producing output.
See http://windowsitpro.com/blog/what-do-not-do-powershell-part-1 and http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx
I have a list of Windows packages that I'm installing via powershell using the following command:
& mypatch.exe /passive /norestart
mypatch.exe is being passed from a list and it doesn't wait for the prior install to finish - it just keeps going. It builds up a huge window of installs that are pending installation. Also, I can't use $LASTEXITCODE to determine if the install succeeded or failed.
Is there anyway to make the installs wait before starting the next?
Start-Process <path to exe> -Wait
JesnG is correct in using start-process,
however as the question showed passing arguments, the line should be:
Start-Process "mypatch.exe" -argumentlist "/passive /norestart" -wait
The OP also mentioned determining if the install succeeded or failed. I find that using a "try, catch throw" to pick up on error states works well in this scenario
try {
Start-Process "mypatch.exe" -argumentlist "/passive /norestart" -wait
} catch {
# Catch will pick up any non zero error code returned
# You can do anything you like in this block to deal with the error, examples below:
# $_ returns the error details
# This will just write the error
Write-Host "mypatch.exe returned the following error $_"
# If you want to pass the error upwards as a system error and abort your powershell script or function
Throw "Aborted mypatch.exe returned $_"
}
Sure, write a one line batch script that runs the installer. The batch script will wait for the installer to finish before returning. Call the script from PowerShell which will in turn wait for the batch script to finish.
If you have access to how mypatch is written, you could have that create some random file when it completes that PowerShell can check for its existence in a while loop and just sleeps while the file doesn't exist.
If you don't, you could also have that batch script create a dummy file when the installer completes.
Yet another way, though probably the worst of all of these is to just hard-code a sleep timer (start-sleep) once you call the installer.
EDIT just saw JensG's answer. Didn't know about that one. Nice
I want my PowerShell script to stop when any of the commands I run fail (like set -e in bash). I'm using both Powershell commands (New-Object System.Net.WebClient) and programs (.\setup.exe).
$ErrorActionPreference = "Stop" will get you part of the way there (i.e. this works great for cmdlets).
However for EXEs you're going to need to check $LastExitCode yourself after every exe invocation and determine whether that failed or not. Unfortunately I don't think PowerShell can help here because on Windows, EXEs aren't terribly consistent on what constitutes a "success" or "failure" exit code. Most follow the UNIX standard of 0 indicating success but not all do. Check out the CheckLastExitCode function in this blog post. You might find it useful.
You should be able to accomplish this by using the statement $ErrorActionPreference = "Stop" at the beginning of your scripts.
The default setting of $ErrorActionPreference is Continue, which is why you are seeing your scripts keep going after errors occur.
Sadly, due to buggy cmdlets like New-RegKey and Clear-Disk, none of these answers are enough. I've currently settled on the following code in a file called ps_support.ps1:
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
function ThrowOnNativeFailure {
if (-not $?)
{
throw 'Native Failure'
}
}
Then in any powershell file, after the CmdletBinding and Param for the file (if present), I have the following:
$ErrorActionPreference = "Stop"
. "$PSScriptRoot\ps_support.ps1"
The duplicated ErrorActionPreference = "Stop" line is intentional. If I've goofed and somehow gotten the path to ps_support.ps1 wrong, that needs to not silently fail!
I keep ps_support.ps1 in a common location for my repo/workspace, so the path to it for the dot-sourcing may change depending on where the current .ps1 file is.
Any native call gets this treatment:
native_call.exe
ThrowOnNativeFailure
Having that file to dot-source has helped me maintain my sanity while writing powershell scripts. :-)
A slight modification to the answer from #alastairtree:
function Invoke-Call {
param (
[scriptblock]$ScriptBlock,
[string]$ErrorAction = $ErrorActionPreference
)
& #ScriptBlock
if (($lastexitcode -ne 0) -and $ErrorAction -eq "Stop") {
exit $lastexitcode
}
}
Invoke-Call -ScriptBlock { dotnet build . } -ErrorAction Stop
The key differences here are:
it uses the Verb-Noun (mimicing Invoke-Command)
implies that it uses the call operator under the covers
mimics -ErrorAction behavior from built in cmdlets
exits with same exit code rather than throwing exception with new message
You need slightly different error handling for powershell functions and for calling exe's, and you need to be sure to tell the caller of your script that it has failed. Building on top of Exec from the library Psake, a script that has the structure below will stop on all errors, and is usable as a base template for most scripts.
Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"
# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
to see if an error occcured. If an error is detected then an exception is thrown.
This function allows you to run command-line programs without having to
explicitly check the $lastexitcode variable.
.EXAMPLE
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
Try {
# Put all your stuff inside here!
# powershell functions called as normal and try..catch reports errors
New-Object System.Net.WebClient
# call exe's and check their exit code using Exec
Exec { setup.exe }
} Catch {
# tell the caller it has all gone wrong
$host.SetShouldExit(-1)
throw
}
I'm new to powershell but this seems to be most effective:
doSomething -arg myArg
if (-not $?) {throw "Failed to doSomething"}
As far as I know, Powershell does not have any automatic handling of non-zero exit codes returned by sub-programs it invokes.
The only solution I know about so far to mimick the behavior of bash -e is to add this check after every call to an external command:
if(!$?) { Exit $LASTEXITCODE }
I came here looking for the same thing. $ErrorActionPreference="Stop" kills my shell immediately when I'd rather see the error message (pause) before it terminates. Falling back on my batch sensibilities:
IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF
I found that this works pretty much the same for my particular ps1 script:
Import-PSSession $Session
If ($? -ne "True") {Pause; Exit}
Seems like simple rethrow does the trick.
param ([string] $Path, [string] $Find, [string] $Replace)
try {
((Get-Content -path $Path -Raw) -replace $Find, $Replace) | Set-Content -Path $Path
Write-Output Completed.
} catch {
# Without try/catch block errors don't interrupt program flow.
throw
}
Now output Completed appears only after successful execution.
for people coming here on 2021 this is my solution that covers both cmdlets and programs
function CheckLastExitCode {
param ([int[]]$SuccessCodes = #(0))
if (!$?) {
Write-Host "Last CMD failed" -ForegroundColor Red
#GoToWrapperDirectory in my code I go back to the original directory that launched the script
exit
}
if ($SuccessCodes -notcontains $LastExitCode) {
Write-Host "EXE RETURNED EXIT CODE $LastExitCode" -ForegroundColor Red
#GoToWrapperDirectory in my code I go back to the original directory that launched the script
exit
}
}
you can use it like this
cd NonExistingpath
CheckLastExitCode
Redirecting stderr to stdout seems to also do the trick without any other commands/scriptblock wrappers although I can't find an explanation why it works that way..
# test.ps1
$ErrorActionPreference = "Stop"
aws s3 ls s3://xxx
echo "==> pass"
aws s3 ls s3://xxx 2>&1
echo "shouldn't be here"
This will output the following as expected (the command aws s3 ... returns $LASTEXITCODE = 255)
PS> .\test.ps1
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
==> pass