Is there a way to show all functions in a PowerShell script? - powershell

Is there any command to list all functions I've created in a script?
Like i created function doXY and function getABC or something like this.
Then I type in the command and it shows:
Function doXY
Function getABC
Would be a cool feature^^
Thanks for all your help.

You can have PowerShell parse your script, and then locate the function definitions in the resulting Abstract Syntax Tree (AST).
Get-Command is probably the easiest way to access the AST:
# Use Get-Command to parse the script
$myScript = Get-Command .\path\to\script.ps1
$scriptAST = $myScript.ScriptBlock.AST
# Search the AST for function definitions
$functionDefinitions = $scriptAST.FindAll({
$args[0] -is [Management.Automation.Language.FunctionDefinitionAst]
}, $false)
# Report function name and line number in the script
$functionDefinitions |ForEach-Object {
Write-Host "Function '$($_.Name)' found on line $($_.StartLineNumber)!"
}
You can also use this to analyze the functions' contents and parameters if necessary.

Where your script is named things.ps1, something like...
cat ./things.ps1 | grep function
For MacOS/Linux or...
cat ./things.ps1 | select-string function
For Windows.

This is a built-in feature as shown in the PowerShell help files.
About_Providers
Similar questions have been asked before. So, this is a potential duplicate of:
How to get a list of custom Powershell functions?
Answers... Using the PSDrive feature
# To get a list of available functions
Get-ChildItem function:\
# To remove a powershell function
# removes `someFunction`
Remove-Item function:\someFunction
Or
Function Get-MyCommands {
Get-Content -Path $profile | Select-String -Pattern "^function.+" | ForEach-Object {
[Regex]::Matches($_, "^function ([a-z.-]+)","IgnoreCase").Groups[1].Value
} | Where-Object { $_ -ine "prompt" } | Sort-Object
}
Or this one
Get List Of Functions From Script
$currentFunctions = Get-ChildItem function:
# dot source your script to load it to the current runspace
. "C:\someScript.ps1"
$scriptFunctions = Get-ChildItem function: | Where-Object { $currentFunctions -notcontains $_ }
$scriptFunctions | ForEach-Object {
& $_.ScriptBlock
}
As for this...
Thanks, this is kind of what i want, but it also shows functions like
A:, B:, Get-Verb, Clear-Host, ...
That is by design. If you want it another way, then you have to code that.
To get name of functions in any script, it has to be loaded into memory first, then you can dot source the definition and get the internals. If you just want the function names, you can use regex to get them.
Or as simple as this...
Function Show-ScriptFunctions
{
[cmdletbinding()]
[Alias('ssf')]
Param
(
[string]$FullPathToScriptFile
)
(Get-Content -Path $FullPathToScriptFile) |
Select-String -Pattern 'function'
}
ssf -FullPathToScriptFile 'D:\Scripts\Format-NumericRange.ps1'
# Results
<#
function Format-NumericRange
function Flush-NumberBuffer
#>

This function will parse all the functions included in a .ps1 file, and then will return objects for each function found.
The output can be piped directly into Invoke-Expression to load the retuned functions into the current scope.
You can also provide an array of desired names, or a Regular Expression to constrain the results.
My use case was I needed a way for loading individual functions from larger scripts, that I don't own, so I could do pester testing.
Note: only tested in PowerShell 7, but I suspect it will work in older versions too.
function Get-Function {
<#
.SYNOPSIS
Returns a named function from a .ps1 file without executing the file
.DESCRIPTION
This is useful where you have a blended file containing functions and executed instructions.
If neither -Names nor -Regex are provided then all functions in the file are returned.
Returned objects can be piped directly into Invoke-Expression which will place them into the current scope.
Returns an array of objects with the following
- .ToString()
- .Name
- .Parameters
- .Body
- .Extent
- .IsFilter
- .IsWorkFlow
- .Parent
.PARAMETER -Names
Array of Strings; Optional
If provided then function objects of these names will be returned
The name must exactly match the provided value
Case Insensitive.
.PARAMETER -Regex
Regular Expression; Optional
If provided then function objects with names that match will be returned
Case Insensitive
.EXAMPLE
Get all the functions names included in the file
Get-Function -name TestA | select name
.EXAMPLE
Import a function into the current scope
Get-Function -name TestA | Invoke-Expression
#>
param (
$File = "c:\fullpath\SomePowerShellScriptFile.ps1"
,
[alias("Name", "FunctionNames", "Functions")]
$Names
,
[alias("NameRegex")]
$Regex
) # end function
# get the script and parse it
$Script = Get-Command /Users/royomi/Documents/dev/javascript/BenderBot_AI/Import-Function.ps1
$AllFunctions = $Script.ScriptBlock.AST.FindAll({$args[0] -is [Management.Automation.Language.FunctionDefinitionAst]}, $false)
# return all requested functions
$AllFunctions | Where-Object { `
( $Names -and $Names -icontains $_.Name ) `
-or ( $Regex -and $Names -imatch $Regex ) `
-or (-not $Names -and -not $Regex) `
} # end where-object
} # end function Get-Function

Related

PowerShell Jobs, writing to a file

Having some problems getting a Start-Job script block to output to a file. The following three lines of code work without any problem:
$about_name = "C:\0\ps_about_name.txt"
$about = get-help about_* | select Name,Synopsis
if (-not (Test-
Path $about_name)) { ($about | select Name | sort Name | Out-String).replace("[Aa]bout_", "") > $about_name }
The file is created in C:\0\
But I need to do a lot of collections like this, so I naturally looked at stacking them in parallel as separate jobs. I followed online examples and so put the last line in the above as a script block invoked by Start-Job:
Start-Job { if (-not (Test-Path $about_name)) { { ($about | select Name | sort Name | Out-String).replace("[Aa]bout_", "") > $about_name } }
The Job is created, goes to status Running, and then to status Completed, but no file is created. Without Start-Job, all works, with Start-Job, nothing... I've tried a lot of variations on this but cannot get it to create the file. Can someone advise what I am doing wrong in this please?
IMO, the simplest way to get around this problem by use of the $using scope modifier.
$about_name = "C:\0\ps_about_name.txt"
$about = get-help about_* | select Name,Synopsis
$sb = { if (-not (Test-Path $using:about_name)) {
$using:about.Name -replace '^about_' | Sort-Object > $using:about_name
}
}
Start-Job -Scriptblock $sb
Explanation:
$using allows you to access local variables in a remote command. This is particularly useful when running Start-Job and Invoke-Command. The syntax is $using:localvariable.
This particular problem is a variable scope issue. Start-Job creates a background job with its own scope. When using -Scriptblock parameter, you are working within that scope. It does not know about variables defined in your current scope/session. Therefore, you must use a technique that will define the variable within the scope, pass in the variable's value, or access the local scope from the script block. You can read more about scopes at About_Scopes.
As an aside, character sets [] are not supported in the .NET .Replace() method. You need to switch to -replace to utilize those. I updated the code to perform the replace using -replace case-insensitively.
HCM's perfectly fine solution uses a technique that passes the value into the job's script block. By defining a parameter within the script block, you can pass a value into that parameter by use of -ArgumentList.
Another option is to just define your variables within the Start-Job script block.
$sb = { $about_name = "C:\0\ps_about_name.txt"
$about = get-help about_* | select Name,Synopsis
if (-not (Test-Path $about_name)) {
$about.Name -replace '^about_' | Sort-Object > $about_name
}
}
Start-Job -Scriptblock $sb
You've got to send your parameters to your job.
This does not work:
$file = "C:\temp\_mytest.txt"
start-job {"_" | out-file $file}
While this does:
$file = "C:\temp\_mytest.txt"
start-job -ArgumentList $file -scriptblock {
Param($file)
"_" | out-file $file
}

How can you cascade a configuration file to submodules in powershell? [duplicate]

In the below sample module file, is there a way to pass the myvar value while importing the module.
For example,
import-module -name .\test.psm1 -?? pass a parameter? e.g value of myvar
#test.psm1
$script:myvar = "hi"
function Show-MyVar {Write-Host $script:myvar}
function Set-MyVar ($Value) {$script:myvar = $Value}
#end test.psm1
(This snippet was copied from another question.)
This worked for me:
You can use the –ArgumentList parameter of the import-module cmdlet to pass arguments when loading a module.
You should use a param block in your module to define your parameters:
param(
[parameter(Position=0,Mandatory=$false)][boolean]$BeQuiet=$true,
[parameter(Position=1,Mandatory=$false)][string]$URL
)
Then call the import-module cmdlet like this:
import-module .\myModule.psm1 -ArgumentList $True,'http://www.microsoft.com'
As may have already noticed, you can only supply values (no names) to –ArgumentList. So you should define you parameters carefully with the position argument.
Reference
The -ArgumentList parameter of Import-Module unfortunately does not accept a [hashtable] or [psobject] or something. A list with fixed postitions is way too static for my liking so I prefer to use a single [hashtable]-argument which has to be "manually dispatched" like this:
param( [parameter(Mandatory=$false)][hashtable]$passedVariables )
# this module uses the following variables that need to be set and passed as [hashtable]:
# BeQuiet, URL, LotsaMore...
$passedVariables.GetEnumerator() |
ForEach-Object { Set-Variable -Name $_.Key -Value $_.Value }
...
The importing module or script does something like this:
...
# variables have been defined at this point
$variablesToPass = #{}
'BeQuiet,URL,LotsaMore' -split ',' |
ForEach-Object { $variablesToPass[$_] = Get-Variable $_ -ValueOnly }
Import-Module TheModule -ArgumentList $variablesToPass
The above code uses the same names in both modules but you could of course easily map the variable names of the importing script arbitrarily to the names that are used in the imported module.

PowerShell alias - No restrictions on name

I have written a simple SetDifference function, by using the Compare-Object function:
Function SetDifference {
param(
[Parameter(Mandatory=$true)]$ReferenceObject,
[Parameter(Mandatory=$true)]$DifferenceObject
)
Compare-Object $ReferenceObject $DifferenceObject |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { $_.InputObject }
}
I've noticed that ANY alias can be used without PowerShell complaining:
Set-Alias '\' 'SetDifference'
Set-Alias '.' 'SetDifference'
Set-Alias '+' 'SetDifference'
Shouldn't there be restrictions on what the alias name can be - to stop you from using symbols that are already part of PowerShell syntax?
You should get an error, when using i.e. the '+'.
See here, you should follow this: "You can use any alphanumeric characters in an alias, but the first character cannot be a number."
Also resuing an already defined alias will throw an error.

Get-Help format is different when calling it in a script

I am wondering why my PowerShell get-help outputs like the following image when using a script I've written. The script's purpose is to display get-help information when selecting a function from an array.
#Run this file in the same directory as the Functions file.
#this function validates user input
function getInput
{
do
{
$input = Read-Host "`n>Enter function # to see its description"
}until(([int]$input -gt 0) -and ([int]$input -le $flist.count))
$input
}
#include the script we want
. "$PSScriptRoot\functions.ps1"
#This operates on a loop. After viewing your help info, press key and you will be prompted to choose another function.
$quit = 0
while(!$quit){
#get all functions
$f = #(get-content functions.ps1 | where-object { $_.StartsWith("function", "CurrentCultureIgnoreCase") -and (-not $_.Contains("#")); $c++} | sort-object)
"There are " + $f.count + " functions!"
#split on ' ', get second word (function name), add to array
$flist = #{}
$i = 0
foreach($line in $f){
$temp = $line.split(' ')
$temp[1]
$i++
$flist.add($i, $temp[1])
}
#print, order ascending
$flist.GetEnumerator() | sort -Property name
#accept user input
$input = getInput
#get-help about the chosen function
"Get-Help " + $flist[[int]$input]
Get-Help Add-ADGrouptoLocalGroup | format-list
#Get-Help $flist[[int]$input] -full
Get-Command $flist[[int]$input] -syntax
Pause
}
The target script $PSScriptRoot\Functions.ps1 has a bunch of functions in it. What my script is doing is this:
List all functions found within a target file.
Put their name in an indexed array
Prompt user for which function to get-help on, at a given index
Print get-help and get-syntax on the selected function
Each function has the <#.SYNOPSIS .DESCRIPTION ... etc #> comment block in it (You can see the function's details--from the function's comment help-block--in the provided image). If I run get-help on the function within the target script, it appears to be formatted normally--but that's not the case when using script I've written.
What is really bothering me is the #{Text = 'stuff'} formatting, etc. Thanks ahead of time!
You're piping the output of get-help through format-list. This "overrides" the default formatting PS does on the PSCustomObject (in PS 3.0 at least) that get-help creates. You should be able to just invoke get-help by itself and not pipe it. If that doesn't work, then pipe it through out-default.
See help about_format for more details.

compile oracle form using powershell script

I have many oracle forms in one folder and I want to compile those forms through frmcmp command in powershell script.
I have written a powershell script which is following
$module="module="
get-childitem "C:\forms\fortest" -recurse |
where { $_.extension -eq ".fmb" } |
foreach {
C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp $module $_.FullName userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\$_.BaseName+'.fmx'
}
but this one is not working. i am new in powershell.
but when I try to compile a single form through command prompt its working like following.
frmcmp module=C:\forms\src\xyz.fmb userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\xyz.fmx
When you want to use variables inside a string in PowerShell you have different options. To start with, you will always need to use " as opposed to ' to wrap the string, if you want variables in your string.
$myVariable = "MyPropertyValue"
Write-Host "The variable has the value $MyVariable"
The above code would yield the output:
The variable has the value MyPropertyValue
If you want to use a property of a variable (or any expression) and insert it into the string, you need to wrap it in the string with $(expression goes here), e.g.
$MyVariable = New-Object PSObject -Property #{ MyPropertyName = 'MyPropertyValue' }
# The following will fail getting the property since it will only consider
# the variable name as code, not the dot or the property name. It will
# therefore ToString the object and append the literal string .MyPropertyName
Write-Host "Failed property value retrieval: $MyVariable.MyPropertyName"
# This will succeed, since it's wrapped as code.
Write-Host "Successful property value retrieval: $($MyVariable.MyPropertyName)"
# You can have any code in those wrappers, for example math.
Write-Host "Maths calculating: 3 * 27 = $( 3 * 27 )"
The above code would yield the following output:
Failed property value retrieval: #{MyPropertyName=MyPropertyValue}.MyPropertyName
Successful property value retrieval: MyPropertyValue
Maths calculating: 3 * 27 = 81
I generally try to use the Start-Process cmdlet when I start processes in PowerShell, since it gives me the possibility of additional control over the process started. This means that you could use something similar to the following.
Get-ChildItem "C:\forms\fortest" -Filter "*.fmb" -recurse | Foreach {
$FormPath = $_.FullName
$ResultingFileName = $_.BaseName
Start-Process -FilePath "C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp.exe" -ArgumentList "module=$FormPath", "userid=xyz/xyz#xyz", "Output_File=C:\forms\11\common\fmx\$ResultingFileName.fmx"
}
You could also add the -Wait parameter to the Start-Process command, if you want to wait with compilation of the next item until the current compilation has completed.