How can I wrap a PowerShell & call to check return code? - powershell

I have a command line executable that I need to call repeatedly in PowerShell with different options.
On every occassion I wish to check that the exit code is 0.
Is there a way for wrapping the call and the parameters as a function?
& bob.bat -b
... error handling
& bob.bat -a -m "a path"
... error handling
Goes to something like:
function callBob ($paramList)
{
& bob.bat $paramList
... error handling
}
callBob -b
callBob -a -m "a path"
etc...
Unfortunately the above code doesn't appear to handle multiple parameters - I can't get things like the second to work, as callBob only takes a single parameter so I end up having to pass in a single string which seems to get quoted on being passed to & bob.bat.

You can do something like this:
function callBob{
bob.bat $args
}
callBob -b
Also, try this wrapper if you want ( from PSAKE):
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}

You can user $args to access parameters:
function callBob
{
Write-Host "Length: $($args.Length)"
Write-Host "arg0: $($args[0])"
Write-Host "arg1: $($args[1])"
Write-Host "arg2: $($args[2])"
}
callBob -b
callBob -a -m "a path"

There are few ways to do it.
1)Create a batch file and call the powershell script from it.
2)Create an exe and then invoke the powershell from it. You can get the exit code in the WPF application and then verify it.
It depends on the complexity and need of your application. I have used a WPF application to wrap the powershell, because we did not want others to run the script by going to powershell and executing. And also we provided a security login in WPF application.
Let me know if you need help if you decided to use WPF Applicaiton.

Related

Store output of command with error

I want to store the output of this command
git checkout master
the output of this command has two states.
success - the command outputs:
Switched to branch 'X'
failure - the command outputs
Your local changes to the following files would be overwritten by checkout <list files> : Please commit your changes or stash them before you switch branches. Aborting
I want to store the output and check if it succeeds by checking for the string abort.
This is what I've tried.
$output = git checkout master 2>&1
if ($output.Contains("Aborting")) {
Write-Host $output -BackgroundColor Red
} else {
Write-Host $output
}
but $output produces something like this, when in the second state:
It seems like the command completed, but it was the wrong syntax. How do I fix this? I would like to to ouput
Make sure that your command's stderr output is converted to regular strings:
$output = [string[]] (git checkout master 2>&1)
Without the [string[]] cast, stderr output lines are stored in $output as [System.Management.Automation.ErrorRecord] instances, and outputting such instances later somewhat misleadingly prints them as if they were PowerShell errors.
(Note that PowerShell Core no longer exhibits this behavior: while stderr lines are still captured as [System.Management.Automation.ErrorRecord] instances, they now print as regular strings.)

How to get PowerShell to display the commands being called

I've got a simple PowerShell script that I'm calling from Jenkins:
function PerformRepoActions($localRepo)
{
$startDir = $pwd.path
cd $localRepo
hg recover
hg revert --all
hg pull -r $branch --rebase
hg update -r $branch
cd $startDir
}
$branch = $env:NAMEDBRANCH
PerformRepoActions $pwd.path
When I run this, it does not show any of the mercurial commands that I'm making. Here's what it shows in the Jenkins output:
no interrupted transaction available
pulling from [repo_location]
no changes found
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
It's giving me the the output of the hg commands, but not showing the commands themselves.
So, I'm looking for something that will make those commands print out, equivalent to the cmd.exe "echo on".
A search tells me that the equivalent in PowerShell is "Set-PSDebug -Trace 1". So I add that to the beginning of the script, and it changes the output to:
DEBUG: 15+ >>>> $branch = $env:NAMEDBRANCH
DEBUG: 16+ >>>> PerformRepoActions $pwd.path
DEBUG: 5+ >>>> {
DEBUG: 6+ >>>> $startDir = $pwd.path
DEBUG: 7+ >>>> cd $localRepo
no interrupted transaction available
pulling from ssh://hg#mercurial.wilcoxassoc.com/PcDmis/QA
no changes found
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
As you can see, while it does give me more output, but it doesn't actually show me the commands that I'm calling.
"Set-PSDebug -Trace 2" gives even more output, but still doesn't show what mercurial commands are being called.
The work-around that I've come up with is to create a function which echoes and the executes a string. It works, but it feels awful kludgey:
function execute($cmd)
{
echo $cmd
iex $cmd
}
function PerformRepoActions($localRepo)
{
$startDir = $pwd.path
execute "cd $localRepo"
execute "hg recover"
execute "hg revert --all"
execute "hg pull -r $branch --rebase"
execute "hg update -r $branch"
execute "cd $startDir"
}
$branch = $env:NAMEDBRANCH
PerformRepoActions $pwd.path
Is there some better way to do this?
It seems that there must be, but I've been surprised before.
edit: This is not a duplicate of PowerShell "echo on". Re-directing the output to a file, the answer to that question, isn't feasible. I need it to display in the Jenkins output, not a file.
there's a way that you can capture the contents of the current script that you're in, and with a little work you could probably marry that up with the relevant output. (whether that's a need I'm not sure)
As of Powershell v3 you have access to the abstract syntax trees (that's the only link I could find that said anything about it!)
Basically the Ast has access all properties of the current script (or any script you send to it) including its complete source.
running this script (I included notepad to show it running external exe)
Write-Host "before"
$MyInvocation.MyCommand.ScriptBlock.Ast
Write-Host "after"
notepad
Will produce this output
before
Attributes : {}
UsingStatements : {}
ParamBlock :
BeginBlock :
ProcessBlock :
EndBlock : Write-Host "before"
$MyInvocation.MyCommand.ScriptBlock.Ast
Write-Host "after"
notepad
DynamicParamBlock :
ScriptRequirements :
ImplementingAssembly :
Extent :
Write-Host "before"
$MyInvocation.MyCommand.ScriptBlock.Ast
Write-Host "after"
notepad
Parent :
after
I imagine if you play around with
$MyInvocation.MyCommand.ScriptBlock.Ast
You should be able to make it produce something like what you want.
edited to add
You could potentially take this a step further by using the Ast to select each Powershell cmdlet and attach a breakpoint to it, then you should have access to the cmdlet names (I don't think that would work with executables - and its only a theory at the moment!) You'd need to run the whole thing through an external script that managed the logging and continuing from the breakpoints. But I think it could work.

Perl: can not get correct exit code from external program

I've searched everywhere, but I can't seem to find a solution for my issue. Probably, it is code related.
I'm trying to catch the exit code from a novell program called DXCMD, to check whether certain "drivers" are running. This is no problem in bash, but I need to write a more complex perl script (easier working with arrays for example).
This is the code:
#Fill the driverarray with the results from ldapsearch (in ldap syntax)
#driverarray =`ldapsearch -x -Z -D "$username" -w "$password" -b "$IDM" -s sub "ObjectClass=DirXML-Driver" dn | grep ^dn:* | sed 's/^....//' | sed 's/cn=//g;s/dc=//g;s/ou=//;s/,/./g'`;
#iterate through drivers and get the exit code:
foreach $driverdn (#driverarray)
{
my $cmd = `/opt/novell/eDirectory/bin/dxcmd -user $username -password $password -getstate "$driverdn"`;
my $driverstatus = $?>>8;
}
I've come this far; the rest of the code is written (getting the states).
But the $?>>8 code always returns 60. When I copy the command directly into the shell and echo the $?, the return code is always 2 (which means the driver is running fine). In bash, the code also works (but without the >>8, obviously).
I've looked into the error code 60, but I cannot find anything, so I think it is due to my code.
How can I rectify this error? Or how can I track the error? Anyone? :)
Wrong value passed to -getstate. You didn't remove the newline. You're missing
chomp(#driverarray);

How do you handle the exception in bat and *sql files

I'm using powershell script to run a few *.sql file. I can get the output value by using the script below..
$return_value = sqlcmd -S ServerName -i "MyAwesome.sql" -v parameter1="par1"
The problem is that I will have to extract out the output text to determine whether there is any error in SQL file or not..
Is there any best practice to handle the exceptions in powershell file and *.sql file?
Do I have to catch the error in each and every SQL files to produce the pre-defined output?
Might not be an option for you, but the Invoke-SqlCmd cmdlet has a parameter called "-ErrorVariable" to trap error messages.
You can use sqlcmd's exit code to determine whether there was an error or not.
$output = sqlcmd -S ServerName -i "MyAwesome.sql" -v parameter1="par1"
if ($LASTEXITCODE -ne 0) {
Write-Error $output
}
If the exit code is not 0 you will usually find the error message either in stdout or stderr. You can put both in the output variable like this:
$output = sqlcmd -S ServerName -i "MyAwesome.sql" -v parameter1="par1" 2>&1
If there was anything in stderr it will be combined with whatever is in stdout.
In your SQL script you will also want to implement best practice error handling techniques such as using try/catch constructs and using transactions where warranted.
http://msdn.microsoft.com/en-us/library/ms179296.aspx
If you catch an exception in your SQL script print the error message and set the return code so you can handle the error in PowerShell.

Call a program from Powershell w/ very long, variable argument list?

I am currently trying to convert a series of batch files to powershell scripts. I would like to run a compiler for the source files that exist in a directory, recursively. The compiler requires a long list of arguments. The catch is, I want the arguments to be variable so I can change them as needed. This is a typical call from the batch file (simplified for readability and length):
"C:\PICC Compilers\picc18.exe" --pass1
"C:\Src Files\somefile.c"
"-IC:\Include Files" "-IC:\Header
Files" -P
--runtime=default,+clear,+init,-keep,+download,+stackwarn,-config,+clib,-plib
--opt=default,+asm,-speed,+space,9 --warn=0 --debugger=realice -Blarge --double=24 --cp=16 -g --asmlist "--errformat=Error [%n] %f; %l.%c
%s" "--msgformat=Advisory[%n] %s" --OBJDIR="C:\Built Files"
"--warnformat=Warning [%n] %f; %l.%c %s"
This command executes fine when included in a batch file, but I start getting errors when I copy and paste the command into powershell. This is only my second day working with powershell, but I have developed with .NET in the past. I have managed to scrape together the following attempt:
$srcFiles = Get-ChildItem . -Recurse -Include "*.c"
$srcFiles | % {
$argList = "--pass1 " + $_.FullName;
$argList += "-IC:\Include Files -IC:\Header Files -P --runtime=default,+clear,+init,-keep,+download,+stackwarn,-config,+clib,-plib --opt=default,+asm,-speed,+space,9 --warn=0 --debugger=realice -Blarge --double=24 --cp=16 -g --asmlist '--errformat=Error [%n] %f; %l.%c %s' '--msgformat=Advisory[%n] %s' '--warnformat=Warning [%n] %f; %l.%c %s"
$argList += "--OBJDIR=" + $_.DirectoryName;
&"C:\PICC Compilers\picc18.exe" $argList }
I know that I probably have multiple issues with the above code, namely how to pass arguments and how I am dealing with the quotes in the argument list. Incorrect as it is, it should illustrate what I am trying to achieve. Any suggestions on where to start?
Calling command line applications from PowerShell might be really tricky. Several weeks ago #Jaykul wrote great blog post The problem with calling legacy/native apps from PowerShell where he describes gotchas which people will meet in this situations. And there is of course solution too ;)
edit - correct url
The article is no more available, so it's only possible to see that through web.archive.org - see cached article
Make $arglist an array instead of a string. A single string will always be passed as a single argument which is what you don't want here.