Trying to call this class to reverse a string in powershell, but can't figure out how to call it.. very simple question but not sure how to do it
class Reverse{
[string]$Stringy
[string]Reversi(){
return $(this.Stringy[-1..-$Stringy.length] -join "")
}
}
Then i am not sure how to call this class. By itself, the return statement does the job, but i need to call the class, please help, no hints just point to the code there is no help online.
My poor attempt to answer this question, as Abraham pointed out in his comment, you need to create an instance of the class:
class ReverseClass {
[string]$Stringy
ReverseClass([string]$String){
$this.Stringy = $String
}
[string]Reverse(){
return -join $this.Stringy[-1..-$this.Length()]
}
[int]Length(){
return $this.Stringy.Length
}
}
$z = [ReverseClass]'Hello World!'
$z.Reverse() # => !dlroW olleH
Again, I insist, a class is an overkill for what you need to do:
function Reverse-String([string]$String){
-join $String[-1..-$String.Length]
}
Reverse-String 'Hello world!' # => !dlrow olleH
If this class was stored on a file like C:\users\user\my documents\script.ps1 and you wanted to load the class or function to your current session you would need to dot source it (simply add a dot before the path). This will load all everything, classes, functions, variables, etc to your current scope.
For example:
If your current directory is not where the script is stored:
. "C:\users\user\my documents\script.ps1"
If you changed directory to my documents in this case:
. .\script.ps1
Are you sure you need a class with an instance method?
If you change the method to be static instead, you can pass whatever string value as a method parameter and get your result without having to create an instance of the class:
class ReverseClass {
static [string] Reverse([string]$string){
return -join $string[-1..-$string.Length]
}
}
Now you can call it using the static member operator :::
PS ~> [ReverseClass]::Reverse("hello")
olleh
Continuing from my comment.
This...
class Reverse{
[string]$Stringy
[string]Reversi(){
return $(this.Stringy[-1..-$Stringy.length] -join "")
}
}
...is not a PowerShell function. This is how you declare a function in PowerShell.
<#
.Synopsis
Short description
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this
.EXAMPLE
Another example of how to use this
#>
function Verb-Noun
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
[string]$Param1,
[int]$Param2
)
}
# calling the function
Verb-Noun -Param1 '' -Param2 ''
For strings, there is no reason to explicitly cast them as strings. Anything thing in quotes (single or double), is automatically seen as a string.
This is all documented in the PowerShell help files...
Get-Help -Name About_functions
... and MS Docs.
Though you can do classes in PowerShell v5 and higher. As documented in the Powershell help files...
Get-Help -Name About_classes
... and MS Docs.
class Reverse{
[string]$Stringy
[string]Reversi(){
return $this.Stringy[-1..-$this.Stringy.length] -join ""
}
}
PS C:> $reverse=New-Object Reverse -Property #{Stringy="Hello World"}
PS C:> $reverse.Reversi()
dlroW olleH
Related
What can I do so that I write the desired phrase in brackets and it will be accepted?
Where exactly to get the "basis" in order to make a return using the "PrintString" method?
# Example 1
$Object.SetString("gamarjoba")
# Example 2
$Object.PrintString()
# Returns
GAMARJOBA
Here is one of my attempts:
class Object {
[string]$gamarjoba
[string]SetString() {
return Write-Host "gamarjoba"
}
[string]PrintString() {
return Write-Host "gamarjoba".ToUpper()
}
}
I understand that this is a very basic question, but I have already spent too much time on it.
You're probably looking for this:
class MyObject {
[string] $gamarjoba = '' # instance variable
# Method has no return type; accepts a string parameter
# and assigns it to instance variable $this.gamarjoba
[void] SetString([string] $gamarjoba) {
$this.gamarjoba = $gamarjoba
}
# Returns the uppercased form of the $this.gamarjoba instance variable.
# Do NOT use Write-Host.
[string] PrintString() {
return $this.gamarjoba.ToUpper()
}
}
$obj = [MyObject]::new() # create an instance
$obj.SetString('aha') # set the string
$obj.PrintString() # print the uppercased string
Note that I've named the class MyObject, because Object would clash with the root class of the .NET type hierarchy.
As for what you tried:
return Write-Host "gamarjoba".ToUpper()
Fundamentally, do NOT use Write-Host to return or output data - it is meant for printing information to the display (console), and bypasses the success output stream and thereby the ability to send output to other commands, capture it in a variable, or redirect it to a file - see this answer for more information.
In the context of class definitions, use only return to return data from a method, passing it an expression or command as-is (e.g, return 'foo'.ToUpper() or return Get-Date -Format yyy)
PowerShell classes participate in the system of output streams only in a very limited way:
returning a value from a method writes it to the success output stream.
Notably, implicit output and use of Write-Output (i.e. what you would use in a script or function) are not supported and quietly ignored. (While you can use return Write-Output ..., there's no good reason to do so.)
throwing an error writes it to the error stream - but you'll only see that if you catch or silence such a fatal-by-default error.
Notably, using Write-Error to write non-terminating errors does not work and is quietly ignored.
However, you can write to all other output streams using their respective Write-* cmdlets, such as Write-Warning.
I would just like to get the validation arguments provided by an IValidateSetValuesGenerator class similar to a return when we use a bad argument (see example command Sample -Verb 'BadVerb')
Below is an example of code:
class verb : System.Management.Automation.IValidateSetValuesGenerator
{
[String[]] GetValidValues()
{
[System.Collections.ArrayList]$Verbs = #()
$VerbsSource = Get-Verb
foreach ($Verb in $VerbsSource)
{
$Verbs.Add([PSCustomObject]#{'verb' = $Verb.Verb})
}
return ($Verbs).Verb
}
}
function Sample
{
[CmdletBinding(SupportsShouldProcess=$true,
PositionalBinding=$false,
ConfirmImpact='Medium')]
[Alias()]
[OutputType([bool])]
Param
(
[Parameter(Mandatory=$true)]
[ValidateSet([verb], ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
[string]$Verb
)
Begin
{
}
Process
{
}
End
{
return $Verb
}
}
Sample -Verb 'BadVerb'
Sample -Verb 'Get'
Mathias R. Jessen has provided the crucial pointer:
# PSv5+ syntax:
# Construct (create an instance of) the [verb] class and call its
# .GetValidValues() instance method.
[verb]::new().GetValidValues() # Returns a [string[]] array of valid values.
Your [verb] class implements the [System.Management.Automation.IValidateSetValuesGenerator] interface (for use in [ValidateSet] attributes to allow constraining parameter values to a dynamically generated set of valid (permissible) values).
This interface, has a single instance method, .GetValidValues(), which returns the permissible values, and which PowerShell calls behind the scenes during parameter validation.
Therefore, in order to call this method yourself, you need to create an instance of your [verb] class first:
In PowerShell v5+, the best choice is to use the static ::new() method, which is PowerShell's way of exposing public constructors; that is, [verb]::new() is equivalent to new verb() in C#.
In older PowerShell versions you must use the New-Object cmdlet for calling constructors; the equivalent of [verb]::new() is New-Object verb
# PowerShell v5+
[verb]::new().GetValidValues()
# PowerShell v4-, but also works in higher versions.
(New-Object verb).GetValidValues()
Syntax pitfalls:
::new() uses method syntax (as in C#), whereas New-Object, as a cmdlet, uses command syntax,[1] i.e. is invoked like a shell command: no (...) around the list of arguments, whitespace as the argument separator.
The following example - using a constructor with arguments - illustrates the difference:
# PSv5+ ::new() call - method syntax.
# Equivalent of this C# constructor call:
# new Regex("^\w+=.+", RegexOptions.Multiline, new Timespan(1000));
[regex]::new('^\w+=.+', [System.Text.RegularExpressions.RegexOptions]::Multiline, [timespan]:new(1000))
# Equivalent New-Object call - command syntax,
# using verbose *named* parameter binding.
# Note the following:
# * absence of (...) around the list of arguments as a whole
# * use of whitespace to separate arguments (and also parameter names from their arguments)
# * the need to separate the *constructor* arguments with ","
# as they must be passed as an *array*.
# * the need to enclose [System.Text.RegularExpressions.RegexOptions]::Multiline individually in (...)
New-Object -TypeName regex -ArgumentList '^\w+=.+', ([System.Text.RegularExpressions.RegexOptions]::Multiline)
# Equivalent call using *positional* (unnamed) parameter binding.
New-Object regex '^\w+=.+', ([System.Text.RegularExpressions.RegexOptions]::Multiline)
A particular pitfall is when a constructor takes a single argument that is an array or a collection, which with New-Object requires wrapping the array in an aux., transitory array:
$array = 1, 2
# OK: Initialize an ArrayList instance via an array that
# binds as a whole to the `System.Collections.ICollection c`
# constructor parameter.
[System.Collections.ArrayList]::new($array)
# !! BROKEN
# $array is interpreted as *multiple* (two separate) arguments.
New-Object System.Collections.ArrayList -ArgumentList $array
# OK
# Need to wrap the array in an aux. transitory array.
New-Object System.Collections.ArrayList -ArgumentList (, $array)
[1] In PowerShell terms, method syntax is parsed in expression mode, whereas command syntax is parsed in argument mode. See the conceptual about_Parsing help topic.
In addition to mklement0's excellent explanation of [Verb]::new(), allow me to provide you with an example of how you could structure the value generator class so that you don't need an instance to generate the values:
class verb : System.Management.Automation.IValidateSetValuesGenerator
{
[String[]] GetValidValues()
{
return [Verb]::GetValidValues()
}
static [String[]] GetValidValues()
{
[System.Collections.ArrayList]$Verbs = #()
$VerbsSource = Get-Verb
foreach ($Verb in $VerbsSource)
{
$Verbs.Add([PSCustomObject]#{'verb' = $Verb.Verb})
}
return ($Verbs).Verb
}
}
Now we've moved the actual heavy lifting to a static method, and the instance method (which is what PowerShell is going to call) simply calls the static method.
Now you can get the value list without creating an instance of [verb]:
PS ~> [verb]::GetValidValues()
Why is the following changing type?
function SomeFunction($SomeParameter){
return $SomeParameter
}
I guess I need to set a return type, but how?
An example is using:
$NewFolder=Join-Path $CurrentFolder -ChildPath $FolderName
$Tmp=SomeFunction($NewFolder)
Now $Tmp is an array and not just a path
While this answer explains the behavior you're seeing, here I will attempt to answer the actual question: how to declare the expected output type of a function!
You do so by adding an [OutputType] attribute to the param() block of your function - so the first thing you'll want to do is to skip the C#-style param list and declare a proper param block instead:
function SomeFunction
{
param($SomeParameter)
return $SomeParameter
}
Now we just need to add the [OutputType] attribute decorator:
function SomeFunction
{
[OutputType([string])]
param($SomeParameter)
return $SomeParameter
}
since we're just returning the parameter argument value as-is in this example, we should play nice and make sure it's actually also a string:
function SomeFunction
{
[OutputType([string])]
param(
[string]$SomeParameter
)
return $SomeParameter
}
Worth noting that [OutputType()] makes no guarantees as to the type of objects emitted during execution, it's simply a way for the author of a function to indicate the intended output type.
Read more about [OutputType] in the about_Functions_OutputTypeAttribute help file
Your issue is per "design". PowerShell will return an array in chunks so that it can be forwarded the PowerShell pipeline.
Example:
SomeFunction -SomeParameter #(1,2,3,4) | Where-Object { $_ -gt 2 }
Without this behavior pipelining the output of the function to another function/cmdlet won't be possible.
If you want to return an array you can change to code to:
function SomeFunction($SomeParameter){
<#
# Through the unary operator we can return an array with one entry.
# This entry contains the original array.
#>
,$SomeParameter
}
Another option would be the use of #() when at the calling side:
function SomeFunction($SomeParameter){
# return to pipelin
$SomeParameter
}
$array = #(SomeFunction -SomeParameter 1,2,3,4)
There is also this reddit answer explaining the behavior in more detail.
Hope that helps.
In my script , I am passing list of components need to taken build
[ValidateSet("InstalComponent","CoreComponent","SDKComponent","ServiceComponent","TimeComponent")]
$Components=#("InstalComponent","CoreComponent"),
From time to time we are adding new component and the list is too long. If there is a way like passing via file "GEt-content Components.txt". It will be of good. Is there any way to set like this?
One option is to create an [enum] from the file contents, then cast the parameter as that type:
#(
"InstalComponent",
"CoreComponent",
"SDKComponent",
"ServiceComponent",
"TimeComponent") |
set-content Components.txt
Add-Type -TypeDefinition #"
public enum MyComponents
{
$(((Get-Content .\Components.txt) -join ', ').split())
}
"#
function test
{
param ( [MyComponents[]]$Components)
$Components
}
Creating and using enums in Powershell
I may not quite understand the question, but you can validate a parameter using ValidateScript like this:
Create a file "Components.txt" with a line for each option on separate lines, with no quotes or commas. Then validate like this:
param(
[ValidateScript({
$Components = Get-Content .\Components.txt
$Components.Contains($_)
})
]
[String]$Component)
...
Short answer: no.
Longer answer: If you must validate a parameter using a configurable set of values you can still do it the old way by putting the validation at the beginning of the function body:
function Foo {
Param(
[Parameter()]$Bar
)
$validSet = Get-Content 'C:\path\to\components.txt'
if ($validSet -notcontains $Bar) {
throw "Invalid argument for parameter -Bar: $Bar"
}
...
}
User cashfoley has posted what appears to be a fairly elegant set of code at codeplex for a "module" called PSClass.
When I dot-source the psclass code into some code of my own, I am able to write code like:
$Animal = New-PSClass Animal {
constructor {
param( $name, $legs )
# ...
}
method -override ToString {
"A $($this.Class.ClassName) named $($this.name) with $($this.Legs) Legs"
}
}
When I tried to create a module out of the PSClass code, however, I started getting errors. The constructor and method names are no longer recognized.
Looking at the actual implementation, what I see is that constructor, method, etc. are actually nested functions inside the New-PSClass function.
Thus, it seems to me that when I dot-source the PSClass.ps1 file, my script-blocks are allowed to contain references to functions nested inside other local functions. But when the PSClass code becomes a module, with the New-PSClass function exported (I tried both using a manifest and using Export-ModuleMember), the names are no longer visible.
Can someone explain to me how the script blocks, scoping rules, and visibility rules for nested functions work together?
Also, kind of separately, is there a better class definition protocol for pure Powershell scripting? (Specifically, one that does not involve "just write it in C# and then do this...")
The variables in your script blocks don't get evaluated until they are executed. If the variables in the script block don't exist in the current scope when the block is executed, the variables won't have any values. Script blocks aren't closures: they don't capture the context at instantiation time.
Remove-variable FooBar
function New-ScriptBlock
{
$FooBar = 1
$scriptBlock = {
Write-Host "FooBar: $FooBar"
}
$FooBar = 2
& $scriptBlock # Outputs FooBar: 2 because $FooBar was set to 2 before invocation
return $scriptBlock
}
function Invoke-ScriptBlock
{
param(
$ScriptBlock
)
& $ScriptBlock
}
$scriptBlock = New-ScriptBlock
& $scriptBlock # Prints nothing since $FooBar doesn't exist in this scope
$FooBar = 3
Invoke-ScriptBlock $scriptBlock # Prints $FooBar: 3 since FooBar set to 3