How do I write a PowerShell script that accepts pipeline input?

I am trying to write a PowerShell script that can get pipeline input (and is expected to do so), but trying something like

ForEach-Object {
# do something
}

doesn't actually work when using the script from the commandline as follows:

1..20 | .\test.ps1

Is there a way?

Note: I know about functions and filters. This is not what I am looking for.

97657 次浏览

You can either write a filter which is a special case of a function like so:

filter SquareIt([int]$num) { $_ * $_ }

or you can create a similar function like so:

function SquareIt([int]$num) {
Begin {
# Executes once before first item in pipeline is processed
}


Process {
# Executes once for each pipeline object
$_ * $_
}


End {
# Executes once after last pipeline object is processed
}
}

The above works as an interactive function definiton or if in a script can be dotted into your global session (or another script). However your example indicated you wanted a script so here it is in a script that is directly usable (no dotting required):

  --- Contents of test.ps1 ---
param([int]$num)


Begin {
# Executes once before first item in pipeline is processed
}


Process {
# Executes once for each pipeline object
$_ * $_
}


End {
# Executes once after last pipeline object is processed
}

With PowerShell V2, this changes a bit with "advanced functions" which embue functions with the same parameter binding features that compiled cmdlets have. See this blog post for an example of the differences. Also note that in this advanced functions case you don't use $_ to access the pipeline object. With advanced functions, pipeline objects get bound to a parameter just like they do with a cmdlet.

This works and there are probably other ways to do it:

foreach ($i in $input) {
$i
}

17:12:42 PS>1..20 | .\cmd-input.ps1
1
2
3
-- snip --
18
19
20

Search for "powershell $input variable" and you will find more information and examples.
A couple are here:
PowerShell Functions and Filters PowerShell Pro!
(see the section on "Using the PowerShell Special Variable “$input”")
"Scripts, functions, and script blocks all have access to the $input variable, which provides an enumerator over the elements in the incoming pipeline. "
or
$input gotchas « Dmitry’s PowerBlog PowerShell and beyond
"... basically $input in an enumerator which provides access to the pipeline you have."

For the PS command line, not the DOS command line Windows Command Processor.

In v2 you can also accept pipeline input (by propertyName or byValue), add parameter aliases etc:

function Get-File{
param(
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)
]
[Alias('FullName')]
[String[]]$FilePath
)


process {
foreach($path in $FilePath)
{
Write-Host "file path is: $path"
}
}
}




# test ValueFromPipelineByPropertyName
dir | Get-File


# test ValueFromPipeline (byValue)


"D:\scripts\s1.txt","D:\scripts\s2.txt" | Get-File


- or -


dir *.txt | foreach {$_.fullname} | Get-File

The following are the simplest possible examples of scripts/functions that use piped input. Each behaves the same as piping to the "echo" cmdlet.

As Scripts:

# Echo-Pipe.ps1
  Begin {
# Executes once before first item in pipeline is processed
}


Process {
# Executes once for each pipeline object
echo $_
}


End {
# Executes once after last pipeline object is processed
}
# Echo-Pipe2.ps1
foreach ($i in $input) {
$i
}

As functions:

Function Echo-Pipe {
Begin {
# Executes once before first item in pipeline is processed
}


Process {
# Executes once for each pipeline object
echo $_
}


End {
# Executes once after last pipeline object is processed
}
}


Function Echo-Pipe2 {
foreach ($i in $input) {
$i
}
}

E.g.

PS > . theFileThatContainsTheFunctions.ps1 # This includes the functions into your session
PS > echo "hello world" | Echo-Pipe
hello world
PS > cat aFileWithThreeTestLines.txt | Echo-Pipe2
The first test line
The second test line
The third test line