How to enable OGV in a powershell function - powershell

How to enable Out-GridView in a function.
I mean,
"Hello" | Out-GridView
Works.
But if I have a simple function like this
function Count ([int]$times)
{
for ($i=1; $i -le $times;$i++)
{
Write-Host $i
}
}
Why calling Count 5 doest not support a pipe to Out-GridView?

The problem you are having is that Write-Host does not output to the pipeline at all. It writes directly to the screen. Replace Write-Host with Write-Output and it should work fine.
BTW, Write-Output is the default so you could just use:
function Count ([int]$times)
{
for ($i=1; $i -le $times;$i++)
{
$i
}
}
or even more simply:
function Count([int]$times)
{
1..$times
}

Related

continue statement confusing in powershell, looks like break statement

My code looks like:
1..10 | % {
$i=$_
10..20 | % {
if ($_ -gt 12) {
continue
}
Write-Host $i $_
}
}
Why the output is:
1 10
1 11
1 12
It seems the continue statement in PoweShell is not different with other language, why PowerShell is designed like this?
If I change the continue to return, then I get the expect result.
As PeterSerAI pointed out in his comment, you don't use a loop in your code, you are instead using the Foreach-Object cmdlet which is different.
Just use a foreach loop instead:
foreach($obj in 1.. 10)
{
$i = $obj
foreach($obj2 in 10 ..20) {
if ($obj2 -gt 12) {
continue
}
Write-Host $obj $obj2
}
}

How to convert infinity while loop to a pipeline statement

I'm trying to use PowerShell pipeline for some recurring tasks and checks, like
perform certain checks X times or skip forward after the response in pipeline will have different state.
The simplest script I can write to do such checks is something like this:
do {
$result=Update-ACMEIdentifier dns1 -ChallengeType dns-01
if($result.Status -ne 'pending')
{
"Hurray"
break
}
"Still pending"
Start-Sleep -s 3
} while ($true)
The question is - how can I write this script as a single pipeline.
It looks like the only thing I need is infinity pipeline to start with:
1..Infinity |
%{ Start-Sleep -Seconds 1 | Out-Null; $_ } |
%{Update-ACMEIdentifier dns1 -ChallengeType dns-01 } |
Select -ExpandProperty Status | ?{$_ -eq 'pending'} |
#some code here to stop infinity producer or stop the pipeline
So is there any simple one-liner, which allows me to put infinity object producer on one side of the pipeline?
Good example of such object may be a tick generator that generates current timestamp into pipeline every 13 seconds
#PetSerAl gave the crucial pointer in a comment on the question: A script block containing an infinite loop, invoked with the call operator (&), creates an infinite source of objects that can be sent through a pipeline:
& { while ($true) { ... } }
A later pipeline segment can then stop the pipeline on demand.
Note:
As of PS v5, only Select-Object is capable of directly stopping a pipeline.
An imperfect generic pipeline-stopping function can be found in this answer of mine.
Using break to stop the pipeline is tricky, because it doesn't just stop the pipeline, but breaks out of any enclosing loop - safe use requires wrapping the pipeline in a dummy loop.
Alternatively, a Boolean variable can be used to terminate the infinite producer.
Here are examples demonstrating each approach:
A working example with Select-Object -First:
& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
This executes Get-Date every second indefinitely, but is stopped by Select-Object after 5 iterations.
An equivalent example with break and a dummy loop:
do {
& { while ($true) { Get-Date; Start-Sleep 1 } } |
% { $i = 0 } { $_; if (++$i -eq 5) { break } } # `break` stops the pipeline and
# breaks out of the dummy loop
} while ($false)
An equivalent example with a Boolean variable that terminates the infinite producer:
& { while (-not $done) { Get-Date; Start-Sleep 1 } } |
% { $done = $false; $i = 0 } { $_; if (++$i -eq 5) { $done = $true } }
Note how even though $done is only initialized in the 2nd pipeline segment - namely in the ForEach-Object (%) cmdlet's (implicit) -Begin block - that initialization still happens before the 1st pipeline segment - the infinite producer - starts executing.Thanks again, #PetSerAl.
Not sure why you'd want to use a pipeline over a loop in this scenario, but it is possible by using a bit of C#; e.g.
$Source = #"
using System.Collections.Generic;
public static class Counter
{
public static bool Running = false;
public static IEnumerable<long> Run()
{
Running = true;
while(Running)
{
for (long l = 0; l <= long.MaxValue; l++)
{
yield return l;
if (!Running) {
break;
}
}
}
}
}
"#
Add-Type -TypeDefinition $Source -Language CSharp
[Counter]::Run() | %{
start-sleep -seconds 1
$_
} | %{
"Hello $_"
if ($_ -eq 12) {
[Counter]::Running = $false;
}
}
NB: Because the numbers are generated in parallel with the pipeline execution it's possible that the generator may create a backlog of numbers before it's stopped. In my testing that didn't happen; but I believe that scenario is possible.
You'll also notice that I've stuck a for loop inside the while loop; that's to ensure that the values produced are valid; i.e. so I don't overrun the max value for the data type.
Update
Per #PetSerAl's comment above, here's an adapted version in pure PowerShell:
$run=$true; &{for($i=0;$run;$i++){$i}} | %{ #infinite loop outputting to pipeline demo
"hello $_";
if($_ -eq 10){"stop";$run=$false <# stopping condition demo #>}
}

Returns objects asyn with Format-Table and a specific layout

I want to return an object with a specific layout, each object should be displayed when returned.
If you run this code in PowerShell (not ISE) you see the result not until the function ends.
function Main
{
for ($i = 1; $i -lt 10; $i++)
{
[pscustomobject]#{Index=$i; TimeStamp=(Get-Date)}
Sleep -Seconds 1
}
}
Main | Format-Table #{Label="Index"; Expression={$_.Index};Width=10},
#{Label="TimeStamp"; Expression={$_.TimeStamp};Width=20}
This works, but I want to a specific layout.
function Main
{
for ($i = 1; $i -lt 10; $i++)
{
[pscustomobject]#{Index=$i; TimeStamp=(Get-Date)}
Sleep -Seconds 1
}
}
Main
And why is this behavior different if you execute this script in the ISE?
This line in my powershell profile was responsible for this behavior.
# The following function automatically adds -AutoSize for when I use Format-Wide or Format-Table
$PSDefaultParameterValues['Format-[wt]*:Autosize'] = $True

PowerShell how to use $MyInvocation for recursion

I am trying to implement some recursive functions with PowerShell. Here is the basic function:
function MyRecursiveFunction {
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
$input
)
if ($input -is [System.Array] -And $input.Length -eq 1) {
$input = $input[0]
}
if ($input -is [System.Array]) {
ForEach ($i in $input) {
$i | ##### HOW DO I USE $MyInvocation HERE TO CALL MyRecursiveFunction??? #####
}
return
}
# Do something with the single object...
}
I have looked at Invoke-Expression and Invoke-Item but have not been able to get the syntax right. For instance I tried
$i | Invoke-Expression $MyInvocation.MyCommand.Name
I'm guessing there is an easy way to do this if you know the right syntax :-)
A bit of an old question, but since there's no satisfactory answer and I've had the same question, here's my experience.
Calling the function by name will break if the name is changed, or if the function is out of scope. It's also not very nice to manage as changing the function name requires editing all the recursive calls, and it'll likely break in modules imported using the prefix option.
# A simple recursive countdown function
function countdown {
param([int]$count)
$count
if ($count -gt 0) { countdown ($count - 1) }
}
# And a way to break it
$foo = ${function:countdown}
function countdown { 'failed' }
& $foo 5
$MyInvocation.InvocationName is slightly nicer to work with, but will still break in the above example (although for different reasons).
The best way would seem to be calling the scriptblock of the function, $MyInvocation.MyCommand.ScriptBlock. That way it'll still work regardless of the function name/scope.
function countdown {
param([int]$count)
$count
if ($count -gt 0) { & $MyInvocation.MyCommand.ScriptBlock ($count - 1) }
}
Just Call the function:
$i | MyRecursiveFunction
To call it without knowing the name of the function you should be able to call it with $myInvocation.InvocationName:
Invoke-Expression "$i | $($myInvocation.InvocationName)"

Is there a short circuit 'or' that returns the first 'true' value?

Scheme has a short-circuiting or that will return the first non-false value:
> (or 10 20 30)
10
> (or #f 20 30)
20
> (or #f)
#f
It does not evaluate its arguments until needed.
Is there something like this already in PowerShell?
Here's an approximation of it:
function or ()
{
foreach ($arg in $args) {
$val = & $arg; if ($val) { $val; break }
}
}
Example:
PS C:\> or { 10 } { 20 } { 30 }
10
Example:
PS C:\> $abc = $null
PS C:\> or { $abc } { 123 }
123
PS C:\> $abc = 456
PS C:\> or { $abc } { 123 }
456
You could do something like this:
10, $false, 20 | ? { $_ -ne $false } | select -First 1
The result is either the first value from the input list that isn't $false, or $null. Since $null is among the values that PowerShell treats as $false in comparisons, the above should do what you want.
As far as I know, there isn't anything like this built in. I think your function looks pretty good.
It might be more idiomatic to make it take pipelined input:
function or
{
foreach ($x in $input) {
$val = & $x; if ($val) { $val; break }
}
}
Example:
PS > $abc = $null
PS > { $abc },{ 123 } | or
123
PS > $abc = 456
PS > { $abc },{ 123 } | or
456
You're trying to make PowerShell use a Scheme-like syntax by way of your function. Don't do this. Write idiomatic PowerShell. Trying to coerce one language into looking like another language just makes things harder on yourself, introduces lots of room for bugs, and will confuse the %$^%&^*( out of whoever has to maintain your code after you're gone.
PowerShell does appear to short-circuit. Put this code in the ISE and set a breakpoint on the write-output lines in each function, then start the debugger (F5):
function first () {
write-output "first"
}
function second() {
write-output "second"
}
$true -or $(first) -or $(second);
$false -or $(first) -or $(second);
$false -or $(second) -or $(first);
$true evaluates to true (obviously), so it doesn't attempt to process the expression beyond that point. When the next to last line processes, only the breakpoint in first processes. When the last line processes, only the breakpoint in second() is hit.
As long as you give the function a good name, I think creating such a function is perfectly idiomatic Powershell. One tweak I would make to OP's implementation is to make passing in a script block optional:
function Select-FirstValue {
$args |
foreach { if ($_ -is [scriptblock]) { & $_ } else { $_ } } |
where { $_ } |
select -First 1
}
Then the caller only has to add { } brackets around arguments that could have side-effects or be performance costly.
I'm using such a function as a simple way to provide default values for params from a config file. It looks something like:
function Do-Something {
param([string]$Arg1)
$Arg1 = Select-FirstValue $Arg1 { Get-ConfigValue 'arg1' } 'default arg1 val'
[...]
}
This way, the config file only is only attempted to be read if the user does not pass in the argument.