Show-Command of commandlet with pre-filled parameters - powershell

Have written a function in powershell with parameters. Would like to use cmdlet Show-command to show UI of the function. But my troubles are to show the function with already pre-filled parameters of my function.
This works well:
Show-Command Get-ChildItem
What I need is:
Show-Command Get-ChildItem -Path "c:\windows"
So the UI of the cmdlet Get-ChildItem should have pre-filled the path parameter of Get-ChildItem cmdlet.
I understand that "-Path" is not a parameter of Show-Command and therefore it doesn't work. But is there any idea or workaround?
Have been trying also to use the the $PSDefaultParameterValues like this:
$PSDefaultParameterValues = #{"Get-childitem:Path" = 'c:\windows'}
Get-ChildItem
this lists the Windows directory correctly, but next command:
Show-Command Get-ChildItem
does not pre-fill the parameter of Path to c:\windows

Related

Powershell: Define set local variable as array

I have a question. I have a setup where I have a parent BAT file that invokes some subroutines I've built, and I'm trying to figure out how to get something particular to work. The short version, in my main BAT file I have this:
#echo off & setlocal
set "deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV', '*.PS1'
call "Subroutines\Find Folders and Remove Files of Extension.bat"
And what I'm looking for is when it invokes the second BAT file, I want it to pass the array to the environment variable. I thought I had it set up the right way to do that:
$deleteThese=$Env:deleteThese
But it's not working quite right. If I specify the deleteThese variable in the subroutine itself, it works fine; if I try to set it in the parent BAT file, it fails out and doesn't do anything. It doesn't even reach the PAUSE flag at the end of the batch file.
This is the syntax in the subroutine BAT file that actually pulls the values and clears the requested files:
$rootPath=$Env:rootPath
$searchFolder=$Env:searchFolder
$deleteThese=$Env:deleteThese
Get-ChildItem -Path $rootPath -Directory -Filter $searchFolder -Recurse | ForEach-Object {
Get-ChildItem -Path $_.FullName -File -Recurse -Include $deleteThese |
Remove-Item -Verbose
}
What am I missing? (Yes I know I could just loop it with a different file extension each time but that seems like it's overcomplicating things.)
The -Include parameter expects an array of strings, but environment variables can only contain a single string value each - so you'll need to split the value into an array before passing it to Get-ChildItem -Include.
In the calling batch file, set the variable (exactly) like this - no single-quotes, and no spaces around =:
set "deleteThese=*.PDF,*.BMP,*.AVI,*.MOV,*.PS1"
Then, in the PowerShell script, make sure to split it into an array of individual strings:
$deleteThese = $Env:deleteThese.Split(',')
Now Get-ChildItem -Include $deleteThese will work as expected

Powershell functions defined in profile

I have a function called GreatFuncion1 defined in "profile.ps1"
loads when powershell launches and works ok
how can I get the path where this function was defined from Powershell?
usually i'll do this with:
(Get-Command -commandtype function -name **GreatFunction1**).Module
but in this case it returns $null, so I can't get a path from here...
?is there any other way??
I need to know the path to the file from what some function was loaded...
Try `${function:GreatFunction1}.File this should give you the full path for wherever your function is coming from
https://stackoverflow.com/a/16008967/13160707
Taking what #nikkelly mentioned in their answer and generalizing it, you could accomplish this by first accessing the ScriptBlock property of the function, and then the File property of the ScriptBlock.
Here are two equivalent examples:
(Get-ChildItem Function: | Where-Object Name -like "**GreatFunction1**").ScriptBlock.File
(Get-Command -CommandType Function -Name **GreatFunction1**).ScriptBlock.File

Can you dynamically set an attribute in a Powershell cmdlet call?

I am not sure if this is possible, but I am wondering if there's an elegant "dynamic" way to use or not an attribute when using a cmdlet in Powershell.
For instance, in the code below, how can i set the -directory attribute to be present or not, depending on some conditions?
gci $folder_root -recurse -directory | ForEach{
# do something
}
You can conditionally add parameter arguments to a call through a technique called splatting.
All you need to do is construct a dictionary-like object and add any parameters you might want to pass to the call there:
# Create empty hashtable to hold conditional arguments
$optionalArguments = #{}
# Conditionally add an argument
if($somethingThatMightBeTrue){
# This is equivalent to having the `-Directory` switch present
$optionalArguments['Directory'] = $true
}
# And invoke the command
Get-ChildItem $folder_root -Recurse #optionalArguments
Notice that any variable that we splat is specified with a # instead of $ at the call site.

How to pipe objects to a specific parameter

I want to list all my PowerShell functions from one directory. The following command works:
Get-ChildItem -Path ($env:USERPROFILE + "\somewhere\*.psm1") -Recurse | ForEach-Object {Get-Command -Module $_.BaseName}
Now I tried to pipe the output from Get-ChildItem directly to the cmdlet Get-Command. Something like this, which does not work:
Get-ChildItem -Path ($env:USERPROFILE + "\somewhere\*.psm1") -Recurse | Get-Command -Module {$_.BaseName}
Obviously, I do not really understand how to pipe the object from Get-ChildItem in the correct way to the parameter -Module in Get-Command.
I have two questions:
Do you have a hint how to pipe correctly?
Is it possible to pipe to a specific parameter like -Module or is the object always handed over to one default parameter?
Parameters can be bound in four different ways:
By location in the argument list, e.g., Get-ChildItem C:\ (only certain parameters)
By name in the argument list, e.g. Get-ChildItem -Path C:\
By value from the pipeline, e.g. 1..5 | Get-Random (only certain parameters)
By name from the pipeline, e.g. 'C:\Windows' | Get-ChildItem (only certain parameters)
You can inspect the various ways of parameter binding via Get-Help <command> -Parameter *. You can then see that Get-Command allows the Module parameter to be bound only by property name:
-Module [<String[]>]
Specifies an array of modules. ...
Required? false
Position? named
Default value none
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
So the input has to be an object that has a Module property, to allow binding. In your case you thus need an additional step in between:
Get-ChildItem -Path ($env:USERPROFILE + "\somewhere\*.psm1") -Recurse |
Select-Object #{l='Module';e={$_.Basename}} |
Get-Command
Now, this instance here is something that's a bit annoying, since the Module parameter is bound by property name, but most things don't give you an object with a Module property. Heck, even Get-Module doesn't have that, since the returned object uses Name as the property name, so you can't even do
Get-Module | Get-Command
However, in many other places (notably concerning paths) work very well automatically. And if you can control your input objects, e.g. when reading from CSV or other data sources, you can end up with rather nice and concise code.
EDIT: Ansgar Wiechers notes that, while this should work, it doesn't, actually. This may be a shortfall of PowerShell's parameter binding algorithm (which is quite complex, as seen above, and we never got it to work correctly in Pash either), or maybe the Get-Command cmdlet has parameters described in a way that simply cannot allow binding because of reasons.

is get-childItem's new -file parameter fast like -filter or slow like -include?

EDIT Hoping here to clarify my convoluted and misleading question... based on my mistaken assumption that -file accepts inputs. Thanks for setting me straight and pointing out that it's just a switch parameter; the inputs in my example actually get passed to -path. Sounds like that may be the fastest purely powershell way to search for multiple file types, since -filter accepts only a single input and -include is slower.
The get-childItem documentation says "Filters are more efficient than other parameters, because the provider applies them when retrieving the objects, rather than having Windows PowerShell filter the objects after they are retrieved."
v3 has a new parameter set with a -file parameter, probably meant for excluding directories, to match cmd.exe's dir /a:-d
Like -include and unlike -filter, -file accepts multiple, as in gci -file "*.ldf","*.bak"
So i'm wondering, and have thus far failed to reliably test, if -file is like -filter from a performance perspective, ie "more efficient", or more like the "other parameters" like -include. If -file is a filter, that's nice because afaict -filter only handles one filter at a time, so if you want multiple filters (like *.ldf and *.bak) then you need to either run gci -filter twice or use -include instead. So I'm wondering if -file lets us reap the efficiency benefits of a filter, for multiple filters.
I stumbled on some error text that's got me optimistic. The -file parameter wants -path to be the current directory, so this gci -path $path -file "*.bak","*.ldf" gives an error. Push-location seems like a viable workaround, but here I'm more interested in the content of the error text:
Get-ChildItem : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Filter'. Specified method is not supported.
I called -file but the error complains about "parameter 'Filter'". So maybe -file is efficient like a filter? OTOH, -filter doesn't need -path to be the current directory, so in that respect -file is more like -include.
just one precision:
gci -path $path -file "*.bak","*.ldf"
-file is a switch parameter (as -directory is) and doesn't accept values (To get only files, use the File parameter and omit the Directory parameter. To exclude files, use the Directory parameter and omit the File parameter); then the "*.bak","*.ldf" are implicitly passed to -filter as value, and filter accept only string and not string[]. That's where your error came from.
Regarding performance: using -file or -directory is faster than using a where-object psiscontainer or ? { !$_.psiscontainer} because it's done at provider (filesystem in this case) level.