79

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Joey
  • 344,408
  • 85
  • 689
  • 683

4 Answers4

122

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
Shay Levy
  • 121,444
  • 32
  • 184
  • 206
  • 1
    Still a function, not a script. See the comment on http://stackoverflow.com/questions/885349/how-to-write-a-powershell-script-that-accepts-pipeline-input/885627#885627. – Joey May 20 '09 at 14:45
  • 6
    no biggie, save the function body in a script file and pipe to the script: dir | .\Get-File.ps1 – Shay Levy May 20 '09 at 20:08
  • 7
    Actually, you can put the param() block at the top of the script without declaring a function local to the script. – Thomas S. Trias Jun 17 '11 at 15:56
44

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.

Bratch
  • 4,103
  • 5
  • 27
  • 32
  • Time for a little necro-ing...just for the benefit of anyone that wanders along and reads this, DOS is still available on every version of windows. 64 bit or 32 bit. – EBGreen Jun 24 '11 at 13:14
  • 14
    EBGreen, `ntvdm`, which is the DOS VM Windows NT has, does not exist on 64-bit systems anymore. `cmd.exe` is *not* DOS; it's the Windows Command Processor and has, apart from grey text on black, nothing in common with DOS at all. – Joey Jun 12 '12 at 20:01
24

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.

Keith Hill
  • 964
  • 5
  • 5
  • I know about filters, but I wanted to use the script as an element in a pipeline, not a function defined in the script. Thanks, though. – Joey May 20 '09 at 05:57
  • 4
    The process block is what you want. It has the advantage of not holding up the pipeline. – JasonMArcher May 21 '09 at 20:43
  • If you specify a param, you should use it. I thought it was part of some kind of parameter prototype and was wondering why it wasn't being used, or if it could be used instead of THIS. Wasted cycles. Still like the answer though ;) – Gerard ONeill Aug 21 '13 at 23:22
8

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
samthebest
  • 30,803
  • 25
  • 102
  • 142
  • Why reiterate an answer that Keith gave two years ago already? – Joey Aug 09 '12 at 11:10
  • His answer contains the following problems: It does not contain exactly that which is necessary to answer the question - the squaring is obtuse. There is no need for the function parameters, we can simply use $_. "You can either write a filter which is a special case of a function like so" - does not make grammatical sense, and using filters is not necessary. – samthebest Aug 09 '12 at 13:49
  • This worked for me on ubuntu. Answers above did not. – n.podbielski Jun 25 '19 at 16:48