How to get PowerShell to display the commands being called - powershell

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.

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.)

cleartool find difference recursive in certain file type to predecessor

In my script I'm calling ClearCase to check if any file of a certain file type in the current path (including all subfolders) has been changed. I'm not familiar with cleartool at all, the command should like this:
cleartool diff -predecessor -recursive *.filetype
As a return value I only need a bool: true if any differences exist, false if nothing has changed
As a return value I only need a bool: true if any differences exist, false if nothing has changed
You need a script. A simple find + exec won't be enough, because the exit status won't be exactly what you need.
#! /bin/bash
noChange=0 ### "cleartool diff" exit status means no difference
files=$(cleartool find . -name "*.filetype")
for f in ${file}; do;
cleartool diff -pre -options "-status_only" "${f}"
if [ $? != $noChange ]; then
exit 0
fi
done
exit 1
(0 is true, false is 1 in shell)
Note the use, in cleartool diff of the -options parameter:
opt/ions pass-through-opts
Specifies one or more compare method options that are not directly supported by diff.
That way, you get only the status from cleartool diff, which is precisely what you want to test in your case.
Since your previous question shows you have Git too, you can easily execute that script in a bash session, even on Windows.
On first glance, the command you're looking for is something like this:
cleartool find . -name "*.filetype" -exec 'cleartool diff -pred "$CLEARCASE_PN"'
On windows, it would look like this:
cleartool find . -name "*.filetype" -exec "cleartool diff -pred \"%CLEARCASE_PN%\""
The single quotes in the Unix example are significant since otherwise the "CLEARCASE_PN" environment variable reference would be expanded at the time you start the command and not when the -exec is actually fired.
The \"%CLEARCASE_PN%\" construct in the Windows command line and the "$CLEARCASE_PN" on unix is to account for filenames including spaces.
Now, looking at what you want... I'm not sure it would accomplish the underlying purpose, whatever that is.

How can I store the output of a command as a variable in PowerShell?

Say I execute the command:
git pshu
which will give me:
git: 'pshu' is not a git command. See 'git --help'.
Did you mean this?
push
If this was the last command I executed, I want retrieve the command and store its outputs in a variable for further use.
So far I have tried things like:
$var = echo (iex (h)[-1].CommandLine)
$var = iex (h)[-1].CommandLine | Out-String
etc.
How can I save such output to a variable?
Lets say you are running multiple commands, and you want to store store a run command based on its input.
Lets say you want to store non existing git commands, as git pshu. My proposal will be to store the command and search for the desired pattern on the output as "Did you mean...". In this case, you can follow as:
$output = git pshu *>&1
if ( "$output".indexOf("Did you mean") -ge 0 ) {
write-host "Command '$($(get-history)[$(get-history).count-1].CommandLine)' didnt exist"
where $(get-history)[$(get-history).count-1].CommandLine will contain the latest run command, in this example, `git pshu'

Compressing to tar.xz using 7-zip through a pipe on windows

My command line is this (powershell):
$7z ="`"c:\Program Files\7-Zip\7z.exe`""
&$7z a -r -ttar -bd -so . | &$7z a -r -txz -bd $archive -si
The produced archive file indeed contains a tar file, but that tar file is corrupt.
Note, that breaking the pipe into two commands works correctly:
&$7z a -r -ttar -bd ${archive}.tmp .
&$7z a -r -txz -bd $archive ${archive}.tmp
The produced archive is perfectly valid.
So, what is wrong with my pipeline?
(I am using Powershell)
Nothing is wrong with your pipeline it is the way that the pipeline works that's causing the error.
PowerShell pipe works in an asynchronous way. Meaning that output of the first command is available to the second command immediately one object at the time even if the first one has not finished executing, See here.
Both Unix and PowerShell pipes operate in the same way. The reason why you might be seeing a difference from Unix to PowerShell is the way in which they go about it is different.
Unix passes Strings between the commands. Where as a Powershell pipe will pass full-fledged .net object between commands. This difference in the data type being past between command will be why it works on unix and not in PowerShell. If 7z.exe can not huddle these .net objects correctly the files will be come corrupt, See here.
Try adding | %{ "$_" } in between the pipes like
&$7z a -r -ttar -bd -so . | %{ "$_" } | &$7z a -r -txz -bd $archive -si
The point is that the second call to 7z expects unmodified data on STDIN, but PowerShell is converting the output from the first call to 7z to (multiple) (string) objects. % is an alias for foreach-object, so what the additional command does is to loop over each object and convert it to a plain string before passing it on to the second call to 7z.
Edit: Reading through PowerShell’s Object Pipeline Corrupts Piped Binary Data it looks to me now as if my suggestion would not work, and there's also no way to fix it. Well, other than wrapping the whole pipeline into a cmd /c "..." call to make cmd and not PowerShell handle the pipeline.
Edit2: I also was trying this solution from the PowerShell Cookbook, but it was very slow.
In the end, I created a .cmd script with the 7z pipes that I'm calling from my PowerShell script.

Can I format powershell output into collapsible Tree View in TeamCity logs?

Is it possible to format powershell output so that it renders as a collapsible section in the TeamCity build log, Tree view?
So for example, my build step uses a powershell runner, and issues a
write-host " ################# deployment manifest ############################"
ls -r -i *.* | %{ $_.FullName }
which outputs this:
[15:28:13] ################# deployment manifest ############################
[15:28:13]\\10.10.10.49\d$\sites\account.foo.net\v32\Bin
[15:28:13]\\10.10.10.49\d$\sites\account.foo.net\v32\contact
[15:28:13]\\10.10.10.49\d$\sites\account.foo.net\v32\Content
[15:28:13]\\10.10.10.49\d$\sites\account.foo.net\v32\controls
[15:28:13]\\10.10.10.49\d$\sites\account.foo.net\v32\error
I'd like that chunk of the log to be collapsible in the Tree View.
Yes we do this with our powershell scripts, you need to get your build script to update Teamcity with the build status. More specifically you need to report the build progress which will tell Teamcity when the start and the end of a block of work occurs. After the build has finished Teamcity will use this information to create nodes on the tree view of the log.
In powershell do the following:
write-host "##teamcity[progressStart '<message>']"
do work
write-host "##teamcity[progressFinish '<message>']"
Note You need to make sure that the message is the same in the start and finish message, blocks can be nested. You can also use the block message instead. I don't know exactly what the difference is but you appear to get the same results:
write-host "##teamcity[blockOpened name='<blockName>']"
do work
write-host "##teamcity[blockClosed name='<blockName>']"