Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h3>Basics: Advanced Functions Can Do More than Standard Functions</h3> <blockquote> <p>Is there a way to write the function so that it works both ways?</p> </blockquote> <p>Yes, using <a href="http://technet.microsoft.com/en-us/library/hh847806.aspx" rel="noreferrer">advanced functions</a>, the process block will be called for each input object.</p> <ul> <li>You need to specify <a href="http://technet.microsoft.com/en-us/library/hh847872.aspx" rel="noreferrer"><code>[CmdletBinding]</code></a> at the start immediately before the <code>param</code> block.</li> <li>You need a parameter that will take pipeline input, this is done with the <a href="http://technet.microsoft.com/en-us/library/hh847743.aspx" rel="noreferrer"><code>Parameter</code></a> attribute on that parameter.</li> </ul> <p>Like this:</p> <pre><code>function ReadInput { [CmdletBinding] param( [Parameter(mandatory=$true, ValueFromPipeline=$true)] $data ) process { "Input was: $data"; } } </code></pre> <hr> <h3>Better to Do Things Natively</h3> <blockquote> <p>head -1 (Print the first line of a file)</p> </blockquote> <p>Look at the <code>First</code> parameter of <code>Select-Object</code>: <code>... | Select -f 1 | ....</code> will just pass the first object through.</p> <blockquote> <p>sed $d (Drop the last line of a file)</p> </blockquote> <p>This one is harder... essentially a function that keeps track if there is another line.</p> <blockquote> <p>perl -pe '$_ = qq($. $_)' (Number lines in a file)</p> </blockquote> <p>You need <code>Measure-Object</code>, which without other parameters will count the number of objects it receives on the pipeline.</p> <hr> <h3>Why It Isn't Working</h3> <p>(Based on the expanded question)</p> <p>There are two parts to this:</p> <p>First: you need to pass the value of the parameter bound to the pipeline into you operation. So:</p> <blockquote> <pre><code>process { perl -pe '$_ = qq($. $_)' } </code></pre> </blockquote> <p>should be</p> <pre><code>process { $data | perl -pe '$_ = qq($. $_)' } </code></pre> <p>Second: this probably won't work for a lot of your utilities, because each time the <code>process</code> block is executed a new instance of the pipeline will be execute, including a new invocation of <code>sed</code> (etc.) for each object on the pipeline and thus losing any state your would normally expect it to maintain from one line to the next.</p> <p>There are two routes around this. First you could use <em>steppable pipelines</em>, which is an advanced topic (and the only decent coverage is in the book <em>Windows PowerShell in Action</em> Second edition by Bruce Payette (who did most of the PSH language design and implementation)).</p> <p>Second: do things natively. Eg. number of lines in a file (without using <code>Measure-Object</code>):</p> <pre><code>function Get-ObjectCount { [CmdletBinding] param( [Parameter(mandatory=$true, ValueFromPipeline=$true)] [object[]]$data # Accept an array... ) begin { $count = 0 # Not strictly needed: PSH will default this. } process { $count += $data.length } end { $count } } </code></pre> <p>This will also be much faster (no need to create another process.</p> <p>As long as you are focused on PSH as a wrapper you are going to find you are gettiong the worst of both worlds: losing the flexibility of the *ix type tools (PSH's execution model is different: cooperating tools in one process) <em>and</em> losing the flexibility of PSH (PSH works on <em>typed objects</em> not strings).</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload