2

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?

1
  • Select-Object {$_.BaseName} -> ForEach-Object {$_.BaseName} Commented May 9, 2017 at 22:03

2 Answers 2

3

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

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome! That explains it!! Thank you very much!
0

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

3 Comments

I created it as a Module, put it in the place where module is supposed to be put and recognized by PS, then I use Import-Module to load the module. I also tried to load it using . .\myFunction.ps1 and it is still having the same behavior. I think the issue might reside in the script and not how the script was loaded. But, I am new to PS, so I might be doing something that I do not suppose to do.
I tried using basename instead of $_.basename and it is still doing the same thing...weird..
I think you misunderstood. I was referring to the "line" where you call you function. What is that line? Select-Object {$_.BaseName}| your_func ?. In any case, you should not be using {$_.baseline } or $_.baseline. Use the last line I mentioned aboe and put your function name insteadof 'whatever'. See if that helps with the error

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.