Indent output from private functions in Powershell - powershell

I have a bunch of functions that I call that produce output that is displayed to the console. Functions might look something like the following:
exec { & .\xunit.console.clr4 tests.xunit }
#or
exec { & .\nuget.exe pack $source_dir\ZocMonLib\NuSpec\ZocMon.nuspec -OutputDirectory $build_dir\local -Symbols -Version $version }
Now I know I could do something like powershell indentation but that only works if I control the output.
How do I do the indenting of output for these private functions?

Ok, I wrote a version that does the line wrapping right. But it's slightly complicated. I posted it on PoshCode http://poshcode.org/3386
That should work for Write-Host or Write-Verbose, but it will not work if those functions are actually outputting objects -- you'd have to pipe to Write-Host.
The function on PoshCode will (optionally) auto-indent based on the stack depth, but also allow you to specify -Pad 5 or something to manually indent, so you can call nuget.exe ... | write-host -pad 5 or just stick | Write-Host wherever you need it, and then set $WriteVerboseAutoIndent = $true ...
Hope that helps -- it does do manual line wrapping on the output of exes, so it should work.

There's not a great solution, because PowerShell doesn't always run in the console window. Other hosting applications might or might not support tab characters, and might not even support Write-Host. If your goal is strictly to support console display, consider writing a "Format-Console" function.
nuget list NuGetPowerTools | Format-Console
Inside that function, you can capture the pipeline input (which I presume would be strings since this is an external command). Each line of output would be a single String object, so...
Write-Host " $x"
Would display that indented by four spaces.
function Format-Console {
[CmdletBinding()]
param([Parameter(ValueFromPipeline=$True)][string[]]$inputObject)
PROCESS { Write-Host " $inputObject" }
}
That's kinda quick and dirty, but assuming you only ever pipe strings to it, it'll work. Building this as a function lets it be more reusable; using the Format- verb cues other users that the output of this isn't intended to be consumable. It technically isn't a true "Format" cmdlet since it doesn't output internal formatting directives, but it's consistent with the usage pattern for

Can't you assign the result of your private functions to a string and "tab" that string?
$x = nuget list NuGetPowerTools
Write-Host "`t`t$x"

Related

Visual Studio Code: Powershell Greenhorn

My Code:
$a = $env:Path
$b = $a.Split(";")
Write-host $b
$a
$b
The automatic Help in Powershell advises me to
Surround with Enum, funtion ....
What am I advised to do ?
An explanation, some sample code could help
Thx in advance
VSCode suggests that you should surround $b[3] with a function because this is stuff that on first glance isn't that maintainable. I see you want the 3rd path of the path variable but don't know what exactly you are looking for, neither does anyone that will potentially maintain the script so you should wrap this line into something meaningfull.
Take this as an example:
function Get-VSCodePath
{
$paths = ($env:Path).Split(';')
$vsCodePath = $paths | Where-Object { ($_.Contains("VS Code") -eq $true) }
Write-Output $vsCodePath
}
$vscodePath = Get-VSCodePath
Write-Host $vscodePath
i wrapped my search for a specific path into a function with meaningfull name so whoever maintains the script will know what i searched for
It's not obvious from your screenshot, but the crucial aspect is that you've selected $b[3].
Whenever you select a multi-character range, Visual Studio's PowerShell extension offers you potentially useful actions, indicated by and accessible via the lightbulb icon:
You can click on the icon or use Control-. (period) to invoke the menu of available actions.
Actions of a general nature that are always presented pertain to surrounding (enclosing) the selected text with various constructs, such as shown in your screenshot.
This is a convenient way to make the selected text part of a larger language construct, such as an if statement or a function definition.
These actions are not prescriptive, i.e. it is up to you to decide whether any of the actions presented make sense in the context of what you're trying to do.

How to read / determine / check the foreground color of pipeline output?

Question
I would like to suppress Yellow host output. How, if at all, can I do the following?
some-cli.exe | Where-Object { $_.ForegroundColor -ne Yellow }
The $_.ForegroundColor -ne Yellow is not supported. What, if anything, is supported?
The some-cli.exe could be anything that produces multicolored output. For instance, it could be choco, nuget, msbuild...
I don't believe this is possible if the foreground color wasn't set in PowerShell itself (and the method of detection in this case escapes me anyway). When you assign the output of an external command which produces color output to a variable for later processing (this would extend to passing the output down the pipeline), the color information is lost. For example, if you have Chocolatey installed (this is one example program I know which produces colorized output) running:
$output = choco install -y nonexistent-package
$output
loses the color which is normally red for the error text provided by choco.exe.
Note: As Jeroen pointed out in comments, "Console applications can explicitly set the color used for console output, but unless they use ANSI terminal sequences for it (supported in the new W10 console only, so not many do) there is no way for hosts to detect it, whether PowerShell or anything else."
However, based on your comments and specific use-case you should be able to pipe the output of nuget restore to a Where-Object clause and filter out any lines starting with WARNING:
nuget restore | Where-Object {
$_ -notmatch '^WARNING'
}
I'm not sure if you tried filtering the way I showed above but based on your comment it sounds like perhaps all of the output might not be separated per line, but retrieved as a raw string for some reason. If that is the case you can make one modification for this to work, split the output on new lines:
( nuget restore ) -split "`r?`n" | Where-Object { ....
The downside to this approach is that if the warning spans multiple lines, it won't catch the subsequent line. The warning would have to be localized to one line for this to work.

Is there a way to echo a Powershell command line?

I've got several Powershell scripts under construction, and one thing I'd like to do in them is spit out a line at the top of the output echoing the command line used.
Use case is: output is being redirected to a file, and a year from now when someone examines that file, I want them to be able to copy/paste the command from the output file to regenerate the same output where the only differences are chronological. [ Okay, that was a little too generic ... first case: I'm examining ACLs and want to be able to repeat the same examination on the newer data at any point in the future by simply copy/pasting the same command. ]
My script begins with the parameter definitions:
[CmdletBinding()]
Param(
[string] $filter="Name -like '*'",
[string] $user=$null,
[switch] $test01=$false,
[switch] $test02=$false
)
What I'm doing now is a fall-back position, knowing what parameters can be accepted, I'm dumping out the names & values of those parameters:
if ($user.Length -eq 0) { $u = "NULL" } else { $u = "|$user|" }
if ($test01) { $u += ", -TEST01" }
if ($test02) { $u += ", -TEST02" }
"RUN BEGINS at $((get-date).ToString('F')) -- Filter is |$filter|, User is $u"
Ugly, hacky, not even a hint of portability in it, and definitely NOT a copy/paste of the command.
Regardless, this CAN be mangled into a command line; but not generically, and not with 100% surety.
I've tried using $args, but apparently the either defining named parameters or the CmdletBinding() breaks that mechanism, because it's always empty. Tried $PsBoundParameters, Get-History, and even $0 .. $9 bash-like variables. So far, nothing I can find gives the command line that launched the script that's running.
$PsBoundParameters is close, it's got all the right data as key,value pairs that could be built up into a command line. but it still isn't a command line, and would require mangling to get it into one.
Get-History came even closer as it includes a complete command line; problem is it gives the command run RIGHT BEFORE the command that launched the script, not the command that launched it.
Running out of options ... but am way open to suggestion.
Found it! And it was as simple as I'd hoped it would be. [ to use ... finding it was a pain ]
$MyInvocation.Line
While I agree that $MyInvocation.Line will get the literal command used which seems to be what you want on the surface, I'd still argue that the data in $PSBoundParameters is more useful long term simply because you can't guarantee users will call your function in a way that makes the command line actually useful.
Consider the common case where callers have declared variables to hold parameter values:
$myfilter = "Name -like '*Joe*'"
MyFunction -filter $myfilter
Consider the case where callers create a hashtable to splat with:
$myParams = #{
filter = "Name -like '*Joe*'"
test01 = $true
}
MyFunction #myParams
If you only record the command line, you'd lose the parameter data in both of these cases. And if you really want a literal command that people can copy/paste from the log, it shouldn't be that hard to generate a synthetic command based on the data in $PSBoundParameters. It doesn't have to be literally the same command as long as the same parameter data gets passed in, right?

How to silence a powershell cmdlet?

I'm writing a script that (by necessity) has to call a rather noisy set of other cmdlets. I'd like to the printing from the other cmdlets to not be displayed so I only see the status messages from my own script.
I've tried > $null and | Out-Null, but those only swallow returned values, not text printed via Write-Host. How can I hide/prevent text being printed "down the stack"?
Try to define your dummy function Write-Host before calling noisy cmdlets, e.g.
function Write-Host {}
If they call Write-Host literally then this should help.
If I've read your post correctly, you'd like to silence the Write-Host cmdlet. If we consider command precedence, we know that functions will be run before cmdlets, if they have the same name. Therefore, I'd recommend you create a Write-Host function that doesn't write anything. Here's an example that highlights this possibility.

Referencing text after script is called within PS1 Script

Let's take the PowerShell statement below as an example:
powershell.exe c:\temp\windowsbroker.ps1 IIS
Is it possible to have it scripted within windowsbroker.ps1 to check for that IIS string, and if it's present to do a specific install script? The broker script would be intended to install different applications depending on what string followed it when it was called.
This may seem like an odd question, but I've been using CloudFormation to spin up application environments and I'm specifying an "ApplicationStack" parameter that will be referenced at the time when the powershell script is run so it knows which script to run to install the correct application during bootup.
What you're trying to do is called argument or parameter handling. In its simplest form PowerShell provides all arguments to a script in the automatic variable $args. That would allow you to check for an argument IIS like this:
if ($args -contains 'iis') {
# do something
}
or like this if you want the check to be case-sensitive (which I wouldn't recommend, since Windows and PowerShell usually aren't):
if ($args -ccontains 'IIS') {
# do something
}
However, since apparently you want to use the argument as a switch to trigger specific behavior of your script, there are better, more sophisticated ways of doing this. You could add a Param() section at the top of your script and check if the parameter was present in the arguments like this (for a list of things to install):
Param(
[Parameter()]
[string[]]$Install
)
$Install | ForEach-Object {
switch ($_) {
'IIS' {
# do something
}
...
}
}
or like this (for a single option):
Param(
[switch]$IIS
)
if ($IIS.IsPresent) {
# do something
}
You'd run the script like this:
powershell "c:\temp\windowsbroker.ps1" -Install "IIS",...
or like this respectively:
powershell "c:\temp\windowsbroker.ps1" -IIS
Usually I'd prefer switches over parameters with array arguments (unless you have a rather extensive list of options), because with the latter you have to worry about spelling of the array elements, whereas with switches you got a built-in spell check.
Using a Param() section will also automatically add a short usage description to your script:
PS C:\temp> Get-Help windowsbroker.ps1
windowsbroker.ps1 [-IIS]
You can further enhance this online help to your script via comment-based help.
Using parameters has a lot of other advantages on top of that (even though they probably aren't of that much use in your scenario). You can do parameter validation, make parameters mandatory, define default values, read values from the pipeline, make parameters depend on other parameters via parameter sets, and so on. See here and here for more information.
Yes, they are called positional parameters. You provide the parameters at the beginning of your script:
Param(
[string]$appToInstall
)
You could then write your script as follows:
switch ($appToInstall){
"IIS" {"Install IIS here"}
}