On the heels of writing about the join-path cmdlet the other day, I thought I would demonstrate a real-world task that deals with the file path differently. I saw this in an article on 4SysOps from a few years back, where the author is not using join-path but instead he uses the ‑LiteralPath parameter to get the same thing done.

But what’s even cooler than that is he demonstrates piping file paths into a PS function, one of the essentials of writing the kinds of devops automation scripts I write on-the-daily. This is a very handy way to have use a function loaded into your interactive PS CLI session, and point paths to it. Great article, I hope you read it!

In today’s example we’re going take the PS function from the article (and, again, I highly recommend a read-through of it), and use it to count the lines of text within each file under a specific path.

Let’s have a look at the code from the article:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function Get-Lines {
    <#
    .SYNOPSIS
    Counts the number of lines in a file.
    #>
    [cmdletbinding(DefaultParameterSetName = 'Path')]
    param(
        [parameter(
            Mandatory,
            ParameterSetName  = 'Path',
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string[]]$Path,
        [parameter(
            Mandatory,
            ParameterSetName = 'LiteralPath',
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('PSPath')]
        [string[]]$LiteralPath
    )
    process {
        # Resolve path(s)
        if ($PSCmdlet.ParameterSetName -eq 'Path') {
            $resolvedPaths = Resolve-Path -Path $Path | Select-Object -ExpandProperty Path
        } elseif ($PSCmdlet.ParameterSetName -eq 'LiteralPath') {
            $resolvedPaths = Resolve-Path -LiteralPath $LiteralPath | Select-Object -ExpandProperty Path
        }
        # Process each item in resolved paths
        foreach ($item in $resolvedPaths) {
            $fileItem = Get-Item -LiteralPath $item
            $content = $fileItem | Get-Content
            [pscustomobject]@{
                Path  = $fileItem.Name
                Lines = $content.Count
            }
        }
    }
}

So the function has a foreach loop to get a line count ( $content.Count ) for each text file processed. The code is place into the Process part of the function, because this allows you to get repeated runs for every item it receives from the <a href=“https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pipelines?view=powershell-7.4"pipeline.

Let’s see this in action. Say I am within a folder somewhere that has 5 files in it, all text files but with varying file types. To pipe everything in that folder, all 5 tezt files, into the function – I need only type:

C:\Temp> Get-ChildItem | Get-Lines

And that would get me:

1
2
3
4
5
6
7
Path        Lines
----        -----
[file].txt      6
codel.ps1       8
code2.ps1      48
Filel.txt       4
file2.txt       5

Let’s just focus on line counts for the “File*” text files:

1
2
3
4
5
6
C:\Temp> Get-Item file* | Get-Lines

Path        Lines
----        -----
Filel.txt       4
file2.txt       5

Or maybe we can make use of -LiteralPath to look at one specific file:

1
2
3
4
5
C:\Temp> Get-Item -LiteralPath [file].txt | Get-Lines

Path        Lines
----        -----
[file].txt      5

Or just go all wildcard for only files ending in txt :

1
2
3
4
5
6
7
C:\Temp> '*.txt' | Get-Lines

Path        Lines
----        -----
[file] .txt     6
filel.txt       4
file2.txt       5

So I hope this clever demo of paths and pipelines inside a PowerShell function helps illustrate just how powerful your scripts and CLI sessions can be. You can be working with data in myriad ways, and automate things to save yourself time.