user1205746
user1205746

Reputation: 3364

Piping parameter issue with powershell

I ran into a weird issue that I do not know how to explain. I hope someone could shed some light for me.

I would like to create a function which takes a parameter of an array, let's say $scriptnames and users have an option to pipe it from previous results should they prefer. I stripped away the unrelated stuff to better illustrate my confusion.

In the process of the module, I read each item from the array and just print the item.

My function:

function Get-Test
{
 [CmdletBinding()]
 param
 (
 [parameter(mandatory=$true,
             ValueFromPipeline=$True,
             ValueFromPipelineByPropertyName=$true)]
  [string[]]$scriptNames
  )
  BEGIN
  {
  }
  PROCESS
  {
    foreach ($scriptName in $scriptNames)
    {
        Write-Verbose "Executing: $scriptname" 
    }    
  }
  END{}

Here was what confused me:

Scenario 1:

I used this command to get the list of the file in my directory:

get-childitem | Select-Object {$_.BaseName}

The list of file was return correctly without extension:

enter image description here

However, when I piped the results to my function, I got this print out:

enter image description here

Notice the unwanted $_.BaseName= literal was added.

Scenario 2:

However, if I issue this command:

get-childitem | Select-Object $_.BaseName

The results did not really filter out the basename only

enter image description here

but when piping to my function:

get-childitem | Select-Object $_.BaseName|
Get-Test -Verbose 

the $_basename literal was not included:

enter image description here

However, the basename included the extension, which really confused me.

Can someone see something flaring up at you? and why did the following things happen:

1) Why was $_.BaseName literal tagged in the print out in scenario 1, which I did not ask for?

2) Why the select did not seem to work in scenario 2, but the print-out did not have $_.baseName with the exact same script?

3) What do I need to correct in my script to print out the filename only without the extension and without the literal $_.BaseName?

Upvotes: 2

Views: 292

Answers (2)

scrthq
scrthq

Reputation: 1076

When you include Select-Object BaseName, your returning an array of objects with 1 property, BaseName. Switch it to Select-Object -ExpandProperty BaseName to do what you're looking for, or you can use (Get-ChildItem).BaseName to achieve the same result.

Whenever you see something expected as a string (i.e. Filename) that's instead returned as an object/hashtable (i.e. @{$_.BaseName=Filename}). If you want to have it return the extension as well, use Name instead of BaseName. If you want the full path to the file, use FullName in that case.

To answer your scenario questions...

  1. That behaved as expected, as you only specified a single property to be returned from the objects, but are still returning objects. The object property is named $_.BaseName because you defined it that way with how you called it (usually you would just need to specify the property name without including the $_. in front to indicate the pipeline object, i.e. Select-Object BaseName). It's also working because of the surrounding curly braces in Scenario 1

  2. The verbose text appears to be defaulting to the Name property, even though you're passing full objects to it. The first example in Scenario 2 returns the full object since you're calling Select-Object incorrectly.

  3. My initial answer should cover the solution, I think

Upvotes: 3

Adil Hindistan
Adil Hindistan

Reputation: 6605

You did not specify how you are calling your function, so cannot really tell what's wrong with #1, it seems like you are returning an array instead of string

#2 is easy. You should have simply wrote

gci |select-object basename

instead of $_.basename.

#3 Probably related to 2.

Also, do not use Select-Object if you are trying to get Basename and do not want the header. You can instead do:

(get-childitem).basename | whatever

Here is how it prints with the example function you provided above

PS C:\> (gci).basename |get-test -ver
VERBOSE: Executing: batch
VERBOSE: Executing: DB
VERBOSE: Executing: Modules
VERBOSE: Executing: nix
VERBOSE: Executing: py
VERBOSE: Executing: utils
VERBOSE: Executing: Microsoft.PowerShellISE_profile
VERBOSE: Executing: Microsoft.PowerShell_profile
VERBOSE: Executing: PowerShellTranscripts

Oh you really should remove Mandatory, if you are expecting to pipe stuff to it or you will get a binding error:

Get-Test : Cannot bind argument to parameter 'scriptNames' because it is an empty string

Upvotes: 0

Related Questions