Powershell conditionals - powershell

I would like to write this in powershell but can not seem to get it to work no matter how I try it. I've used "try and catch" but it doesn't seem to work the way I want.
Try
{
write-host Attempting Reset
CD C:\MKC1_ECU_Reuse
./power_on.bat
CD C:\UTIL\XDL\XMIT\BIN
.\xpc32_nodriver conm2.xpc -con
.\xpc32_nodriver $global:path\$global:customer\$global:customer_reset.xpc
}
Catch
{
Write-warning "Something went wrong!: $_"
return # script failed
}
In Batch, it would look like this:
Do some task || goto :error
goto :finished
Powershell will try to do the task and then go to the next line regardless of if the task completed successfully. The task I am trying to do is to connect to an EEPROM and write to it. If the write fails, I do not want to continue my script. I would rather be able to display an error telling the operator that there was an issue. I am working with what my company allows me to work with or I would use the newest Powershell that allows for || and &&. Any help would be appreciative.

Inside the batch file you may need something like:
IF %ERRORLEVEL% NEQ 0 (
exit %ERRORLEVEL%
)
That should relay the elevated exit code back to PowerShell in the $LASTEXITCODE variable. Then you may be able to use the logic from the previous answer.
I guess they are forcing you to use .bat, but it might be better is you launched the underlying program from inside PowerShell.

Try/Catch only works with teminating powershell command errors. You can test
if ($LASTEXITCODE -ne 0) or if (-not $?) for an error.
Taking a guess at an answer:
$result = .\xpc32_nodriver $global:path\$global:customer\$global:customer_reset.xpc
if ($result -like '*error*') {
write-warning 'Something went wrong!'
exit 1
}

Related

How to break out of script using invoke-expression

I have a powershell script that works like a menu. After making a choice, it starts another powershell script.
The other scripts are fairly large, and I have many points in the scripts where I need to abort that script and return back to the menu.
I call the other scripts using:
invoke-expression -Command "& '.\script.ps1'"
In the script itself, at various locations, I use the following command to break out of the script.
break script
This works well, as it terminates the script, but it does not return to the previous invoked script.
The break command with parameter means that it breaks to a label. If no label is present it will break out of the entire script. But given how I invoke the script currently, it sees everything as one big script and breaks out of everything.
In my subscript, I placed the following at the end of the file:
:End_Of_Script
and used break End_Of_Script but that fails the same.
So my question is boiling down to this:
How can I invoke another script and at any point break out of that script and return back to the previous script without altering the complete structure of that script?
Here's a snippet of what the subscript is:
write-host "1. Choice A"
write-host "2. Choice B"
$choice = read-host "What choice?"
switch ($choice)
1
{
write-host "The choice is A"
}
2
{
write-host "The choice is B"
}
default
{
write-host "Invalid choice"
break script
}
get-childitem | foreach-object {
if( $_.Name -like "*test*")
{
break script
}
}
In my main script, the commands after invoke-expression ... are never executed.
You can use exit to do this.
The exit keyword is best used in functions, and when those functions are called in a script. It's a great way to terminate execution of functions.
reference PowerShell Exit Function & Code Execution | Pluralsight
If the script encounters an error and needs to exit, throw can be used to better prompt the specific cause of the error.
Calling another script can be done with & $FilePath, invoke-expression -Command "& '$FilePath'" is a cumbersome and unnecessary usage.

Can you redirect errors in a powershell script when $ErrorActionPreference is set to stop?

I have some powershell scripts with the following in them:
$ErrorActionPreference = "stop"
trap { Write-Error $_.Exception; }
When calling the script from a powershell window using a powershell redirection operator:
c:\somescript.ps1 2> c:\error.txt
It will only print out the error to the powershell window and not redirect it to the file. If I remove $ErrorActionPreference = "stop" from the script, then the redirection works, but that isn't a viable option since we need the scripts to terminate if it runs into any error. I've tried using '*>' too, but that produces the same results.
Is there any way to redirect the errors to a file without having to change all the scripts?
The problem with your approach is that $ErrorActionPreference = "Stop":
instantly stops (aborts) the entire script and the command that calls it, c:\somescript.ps1 2> c:\error.txt,
which means that the redirection 2> c:\error.txt is not performed, and the error message displays in the console rather than being captured in the specified file.
The workaround is somewhat cumbersome:
Wrap the invocation of the other script in a try ... catch statement as follows:Tip of the hat to FatalBulletHit for his help.
$ErrorActionPreference = 'Stop'
try {
c:\somescript.ps1
} catch {
# Now you can write the error record to the file.
$_ > c:\error.txt
# == Optional additional actions.
# If desired, you can additionally
# emit the error too (write it the error to the error stream).
# !! -ErrorAction Continue is necessary in order to override
# !! $ErrorActionPreference = 'Stop'.
# !! Without it, the use of Write-Error itself would cause an - uncaught -
# !! script-terminating (fatal) error.
Write-Error -ErrorRecord $_ -ErrorAction Continue
# Exit the script with a nonzero exit code to signal failure
# (which is useful if your script is called from *outside*
# PowerShell).
exit 1
}
The need to use -ErrorAction Continue (or temporarily set $ErrorActionPreference = 'Continue') before calling Write-Error in the catch block to avoid causing a script-terminating (fatal) error is surprising; it would also apply if your script / function is an advanced one and you used $PSCmdlet.WriteError($_) instead.
You might want to have a look at using Try Catch Finally for Error Trapping.
I have had the most success with that.
Great TechNet Blog on this subject

Conditionally piping to Out-Null

I'm writing a PowerShell script to msbuild a bunch of solutions. I want to count how many solutions build successfully and how many fail. I also want to see the compiler errors, but only from the first one that fails (I'm assuming the others will usually have similar errors and I don't want to clutter my output).
My question is about how to run an external command (msbuild in this case), but conditionally pipe its output. If I'm running it and haven't gotten any failures yet, I don't want to pipe its output; I want it to output directly to the console, with no redirection, so it will color-code its output. (Like many programs, msbuild turns off color-coding if it sees that its stdout is redirected.) But if I have gotten failures before, I want to pipe to Out-Null.
Obviously I could do this:
if ($SolutionsWithErrors -eq 0) {
msbuild $Path /nologo /v:q /consoleloggerparameters:ErrorsOnly
} else {
msbuild $Path /nologo /v:q /consoleloggerparameters:ErrorsOnly | Out-Null
}
But it seems like there's got to be a way to do it without the duplication. (Okay, it doesn't have to be duplication -- I could leave off /consoleloggerparameters if I'm piping to null anyway -- but you get the idea.)
There may be other ways to solve this, but for today, I specifically want to know: is there a way to run a command, but only pipe its output if a certain condition is met (and otherwise not pipe it or redirect its output at all, so it can do fancy stuff like color-coded output)?
You can define the output command as a variable and use either Out-Default or Out-Null:
# set the output command depending on the condition
$output = if ($SolutionsWithErrors -eq 0) {'Out-Default'} else {'Out-Null'}
# invoke the command with the variable output
msbuild $Path /nologo /v:q /consoleloggerparameters:ErrorsOnly | & $output
UPDATE
The above code loses MSBuild colors. In order to preserve colors and yet avoid
duplication of code this approach can be used:
# define the command once as a script block
$command = {msbuild $Path /nologo /v:q /consoleloggerparameters:ErrorsOnly}
# invoke the command with output depending on the condition
if ($SolutionsWithErrors -eq 0) {& $command} else {& $command | Out-Null}
is there a way to run a command, but only pipe its output if a certain condition is met (and otherwise not pipe it or redirect its output at all, so it can do fancy stuff like color-coded output)?
There is no such a way built-in, more likely. But it can be implemented with a function and the function is reused as such a way:
function Invoke-WithOutput($OutputCondition, $Command) {
if ($OutputCondition) { & $Command } else { $null = & $Command }
}
Invoke-WithOutput ($SolutionsWithErrors -eq 0) {
msbuild $Path /nologo /v:q /consoleloggerparameters:ErrorsOnly
}

Grunt | Throw Compilation Error to Powershell

I am trying to call Grunt command from my powershell script. But in case of any complilation failure Grunt is printing the error code to the console but not returning anything to powershell command line and even not going to catch block.
Please let me know how to resolve this issue.
Noah's answer (using $ErrorActionPreference) can work. Alternatively you can use the automatic variables $? and/or $LastExitCode immediately after executing the external command to take a specific action if it failed. This doesn't require a try-catch block For example:
Grunt.exe -whatever
if (!$?) { throw "Grunt command returned $LastExitCode" }
Without seeing a screenshot of what is happening or more details it is hard to say what is going on here. However, the basics of PS are that you need to make sure the error is considered terminating for the catch block to trigger. Since this does not sound like a native PS cmdlet you can force it to be a terminating error like below:
Try {
$ErrorActionPreference = 'Stop'
Grunt.exe whatever
$ErrorActionPreference = 'Continue'
}
Catch {stuff}

Execute process conditionally in Windows PowerShell (e.g. the && and || operators in Bash)

I'm wondering if anybody knows of a way to conditionally execute a program depending on the exit success/failure of the previous program. Is there any way for me to execute a program2 immediately after program1 if program1 exits successfully without testing the LASTEXITCODE variable? I tried the -band and -and operators to no avail, though I had a feeling they wouldn't work anyway, and the best substitute is a combination of a semicolon and an if statement. I mean, when it comes to building a package somewhat automatically from source on Linux, the && operator can't be beaten:
# Configure a package, compile it and install it
./configure && make && sudo make install
PowerShell would require me to do the following, assuming I could actually use the same build system in PowerShell:
# Configure a package, compile it and install it
.\configure ; if ($LASTEXITCODE -eq 0) { make ; if ($LASTEXITCODE -eq 0) { sudo make install } }
Sure, I could use multiple lines, save it in a file and execute the script, but the idea is for it to be concise (save keystrokes). Perhaps it's just a difference between PowerShell and Bash (and even the built-in Windows command prompt which supports the && operator) I'll need to adjust to, but if there's a cleaner way to do it, I'd love to know.
You could create a function to do this, but there is not a direct way to do it that I know of.
function run-conditionally($commands) {
$ranAll = $false
foreach($command in $commands) {
invoke-command $command
if ($LASTEXITCODE -ne 0) {
$ranAll = $false
break;
}
$ranAll = $true
}
Write-Host "Finished: $ranAll"
return $ranAll
}
Then call it similar to
run-conditionally(#(".\configure","make","sudo make install"))
There are probably a few errors there this is off the cuff without a powershell environment handy.
I was really hurting for the lack of &&, too, so wrote the following simple script based on GrayWizardX's answer (which doesn't work as-is):
foreach( $command in $args )
{
$error.clear()
invoke-command $command
if ($error) { break; }
}
If you save it as rc.ps1 (for "run conditionally") in a directory in your path, you can use it like:
rc {.\configure} {make} {make install}
Using script blocks (the curly braces) as arguments instead of strings means you can use tab completion while typing out the commands, which is much nicer. This script is almost as good as &&, and works.