Powershell function reference and naming conventions - powershell

I recently discovered that you can get powershell's functions by reference using the modifier $function:. But I noticed a strange problem...
By convention, POSH uses a {Verb}-{Noun} convention for function names, where {Verb} is part of aproved POSH verb names. For instance: Get-Member or Invoke-WebRequest.
The thing is, that calling $function:Get-Member, for example, is not valid because of the hyphen; it works just fine if you declare a function like ShowMessage and calls: $fn = $function:ShowMessage. So I'd like to know if there's a way to escape this call.
PS.: I do know of another option, but is much much more verbose:
function Show-Message { Write-Host "Foo"; }
$fn = (Get-Item "function:Show-Message").ScriptBlock;
$fn.Invoke();
Update: Although #PetSerAl was very helpfull and explained the problem, I'll mark #Ansgar Wiechers's response as the answer because it's better documented.

function: is a PSDrive, not a (scope)modifier. As for using it with variable-like notation ($function:name): the rules for variable names with special characters apply here as well.
From the documentation:
VARIABLE NAMES THAT INCLUDE SPECIAL CHARACTERS
Variable names begin with a dollar sign. They can include alphanumeric characters and special characters. The length of the variable name is limited only by available memory.
Whenever possible, variable names should include only alphanumeric characters and the underscore character (_).Variable names that include spaces and other special characters, are difficult to use and should be avoided.
To create or display a variable name that includes spaces or special characters, enclose the variable name in braces. This directs PowerShell to interpret the characters in the variable name literally.
Your notation should thus look like this:
${function:Show-Message}
It can be invoked like this:
& ${function:Show-Message}

Related

How to set an env var containing a special character in powershell? [duplicate]

Exploring the difference between help and get-help I did:
cd Function:
get-content help
all the input-parameter are defined like: [string]${Name}
$=initiate a variable, {} a hashtable??
Thanks for your help.
For the official documentation, see the conceptual about_Variables help topic (invoke it with help about_Variables), and in particular its "Variable Names that Include Special Characters" section.
Enclosing the name of a variable in {...} - e.g. ${foo} - unambiguously delimits the variable name (foo).
While you can use this notation with any variable reference, doing so is required in the following scenarios:
If the name contains unusual characters, such as - or . (see the linked help topic for the exact set of permissible characters); e.g.:
${foo-bar}
${foo.bar}
If the variable reference is embedded in an expandable string ("..."), you may need to tell PowerShell where the variable name ends, if the immediately following characters would otherwise be interpreted as part of the variable name; e.g.:
"${foo}: bar" - without the {...}, PowerShell would interpret $foo: as an (incomplete) variable name, which fails, because foo is then interpreted as the name of a PS drive in the context of namespace variable notation.
Note: An alternative in this case is to `-escape the : character: "$foo`: bar"
"A ${foo}l and his money ..." - without the {...}, PowerShell would look for variable $fool instead.
While in your example (${Name}) enclosing in {...} is not necessary, the reason that it is used is that the code was automatically generated as a proxy function that wraps the Get-Help cmdlet, and this generation mechanism methodically encloses all variables in {...}.

What am I allowed to name a function in Powershell?

PS > function ]{1}
PS > ]
1
PS >
PS
Why does this work?
What else can I name a function? All I've found so far that works is * and ].
You can name it almost anything. You can even include newlines and emoji* in the name.
function Weird`nFunctionの名前😀 { Write-Host hey }
$c = gcm Weird*
$c.Name
& $c
Escaping helps with lots of things like that:
function `{ { Write-Host cool }
`{
function `0 { Write-Host null }
gci function:\?
I'll add that this is true for variables too, and there's a syntax that removes the need to do most escaping in the variable name: ${varname} (as opposed to $varname).
With that, you could easily do:
${My variable has a first name,
it's
V
A
something
R,
whatever I dunno
🤷} = Get-Process
You'll note that if you then start typing like $MyTAB it will tab complete in a usable way.
To (somewhat) answer why this should work, consider that the variable names themselves are just stored in .Net strings. With that in mind, why should there be a limit on the name?
There will be limits on how some of these names can be used in certain contexts, because the parser will not understand what to do with it if the names don't have certain characters escaped, but literal parsing of PowerShell scripts are not the only way to use functions or variables or other language constructs, as I've shown some examples of.
Being less limiting also means being able to support other languages and cultures by having wide support for character sets.
To this end, here's one more thing that might surprise you: there are many different characters to represent the same or similar things that we take for granted in code, like quotation marks for example.
Some (human) languages or cultures just don't use the same quote characters we do in English, don't even have them on the keyboard. How annoying would it be to type code if you have to keep switching your keyboard layout or use ALT codes to quote strings?
So what I'm getting at here is that PowerShell actually does support many quote characters, for instance, what do you think this might do:
'Hello’
Pretty obvious it's not the "right" set of quotes on the right side. But surprisingly, this works just fine, even though they aren't the same character.
This does have important implications if you're ever generating code from user input and want to avoid sneaky injection attacks.
Imaging you did something like this:
Invoke-Expression "echo '$($userMsg -replace "'","''")'"
Looks like you took care of business, but now imagine if $userMsg contained this:
Hi’; gci c: -recurse|ri -force -whatif;'
For what it's worth, the CodeGeneration class is aware of this stuff ;)
Invoke-Expression "echo '$([System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($userMsg))'"
* PowerShell Console doesn't have good support for Unicode, even though the language does. Use ISE to better see the characters.

Powershell - add variables inside a json string

I have the following json code in my powershell script.
I set the $variable to 1111111111
$jsonfile = '{"Version": "2012-10-17","Statement": {"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::$variable:role/xxxxxx"}}'
The output gives ....arn:aws:iam::$variable:role/xxxxxx..... instead of ....arn:aws:iam::1111111111:role/xxxxxx
The problem is that I must use the single quote for the json string otherwise I will get an error. If I use single quote I wont be able to put the variables inside the string. How do I workaround this problem?
There are various ways to solve your problem, but perhaps the easiest approach is to use PowerShell's string interpolation:
use a double-quoted string overall to enable interpolation of embedded variable references and subexpressions ($(...)).
escape embedded " chars. as `" (using backticks)
disambiguate variable references by enclosing the variable name in {...}.
Simplified example:
PS> $variable='111'
PS> "{`"Version`": `"arn:aws:iam::${variable}:role/xxxxxx`"}}"
{"Version": "arn:aws:iam::111:role/xxxxxx"}}
Note that enclosing variable names in {...} in interpolated strings is only necessary if the following char. could be misinterpreted as part of the variable name.
A : following the variable name - as is the case here - is such a case, because PS variables can have a scope specifier preceding the variable name that is separated from the variable name with :, such as in $env:USERNAME.
DAXaholic's helpful answer shows an alternative based on PowerShell's binary -f operator, which is essentially the same as the .NET framework's String.Format method; as such:
it introduces additional complexity, such as needing to know what its escaping rules are ({ chars. must be escape as {{, and how to format its arguments specified on the RHS of -r ({0} refers to the 1st RHS argument, ...)
on the flip side, -f offers many sophisticated formatting options.
Also, consider use of the Convert*-Json cmdlets his answer demonstrates: even though they're less performant, they ultimately make manipulation of JSON much easier and more robust.
Alternatives in the realm of native PowerShell code:
String concatenation with the binary + operator:
'{"Version": "arn:aws:iam::' + $variable + ':role/xxxxxx"}}'
String templating with $ExecutionContext.InvokeCommand.ExpandString():
$variable='111'
$tmpl = '{"Version": "arn:aws:iam::${variable}:role/xxxxxx"}}' # string template *literal*
$ExecutionContext.InvokeCommand.ExpandString($tmpl) # performs on-demand interpolation
Another solution would be
$jsonfile = '{{"Version": "2012-10-17","Statement": {{"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::{0}:role/xxxxxx"}}}}' -f $variable
So you have to escape the braces with another brace but in your case you have fewer braces than quotes so it is "less obfuscation" :)
In your case, maybe the simplest solution is just concatenating the strings together instead of using string formatting / interpolation.
In addition you could also go the way with the JSON cmdlets:
$jsonfile |
ConvertFrom-Json |
% { $_.Statement.Resource = "arn:aws:iam::${variable}:role/xxxxxx"; $_ } |
ConvertTo-Json

Warning Control Character '\S' is not valid when concatinating two strings

I have two variables such as:
path='data\voc11\SegmentationClassExt\%s.png'
name='123'
I want to concatenate two strings into one like so:
data\voc11\SegmentationClassExt\123.png
I used the code below:
sprintf(path, name)
However I receive the following error:
Warning: Control Character '\S' is not valid. See 'doc sprintf' for control characters valid in the format string.
ans =
dataoc11
I am using MATLAB on Windows. Could you give me any solution for that. I tried to change path='data\\voc11\\SegmentationClassExt\\%s.png' and when I did that, the above code will work. However, the current data is
path='data\voc11\SegmentationClassExt\%s.png';
use the matlab function fullfile
filename = fullfile ( path, [name '.png'] );
or
filename = fullfile ( path, sprintf ( '%s.png', name ) );
Note: you should avoid using path as a variable as it is already a Matlab function
Before we start, it's highly advised that you do not use path as a local variable. path is a global variable that MATLAB uses to resolve function scope, especially if you are going to use any functions from toolboxes. Overwriting path with your own string will actually make MATLAB not function properly. Use a different variable name.
Now to resolve your problem, you can use either fullfile as what #matlabgui has suggested, or if you don't care about OS compatibility and are only working in Windows, you can either manually change the path as you have placed so that you can introduce two back slashes and it will indeed work on Windows OS, or you can perhaps use a string replace function so that all back slashes will be accompanied with an additional back slash.
Either one of these two methods will work:
Method 1 - Using regular expressions
pat = 'data\voc11\SegmentationClassExt\%s.png';
pat_new = regexprep(pat, '\\', '\\\\');
The function regexprep performs a string replacement by regular expressions. We search for all single backslashes and replace them with double backslashes. Note that the single back slash \ is a special character in regular expressions so if you explicitly what to look for back slashes, you must place an additional back slash beside it.
Method 2 - Using strrep
pat = 'data\voc11\SegmentationClassExt\%s.png';
pat_new = strrep(pat, '\', '\\');
strrep stands for String Replace. It works very similar to regular expressions as we have discussed above. However, what's nice is that you don't have to append an additional back slash when looking for the actual character.
Once you do this, you can use sprintf as normal:
pat_new = sprintf(pat_new, name);

What is the meaning of ${} in powershell?

I have a script where function parameters are expressed like this:
param(
${param1},
${param2},
${param3}
)
What does it mean? I have been unable to find documentation on this.
What's the point of writing parameters that way instead of the more usual
param(
$param1,
$param2,
$param3
)
?
#MikeZ's answer is quite correct in explaining the example in the question, but as far as addressing the question title, there is actually more to say! The ${} notation actually has two uses; the second one is a hidden gem of PowerShell:
That is, you can use this bracket notation to do file I/O operations if you provide a drive-qualified path, as defined in the MSDN page Provider Paths.
(The above image comes from the Complete Guide to PowerShell Punctuation, a one-page wallchart freely available for download, attached to my recent article at Simple-Talk.com.)
They are both just parameter declarations. The two snippets are equivalent. Either syntax can be used here, however the braced form allows characters that would not otherwise be legal in variable names. From the PowerShell 3.0 language specification:
There are two ways of writing a variable name: A braced variable name, which begins with $, followed by a curly bracket-delimited set of one or more almost-arbitrary characters; and an ordinary variable name, which also begins with $, followed by a set of one or more characters from a more restrictive set than a braced variable name allows. Every ordinary variable name can be expressed using a corresponding braced variable name.
From about_Variables
To create or display a variable name that includes spaces or special characters, enclose the variable name in braces. This directs Windows PowerShell to interpret the characters in the variable name literally.
For example, the following command creates and then displays a variable named "save-items".
C:\PS> ${save-items} = "a", "b", "c"
C:\PS> ${save-items}
a
b
c
They are equivalent. It's just an alternative way of declaring a variable.
If you have characters that are illegal in a normal variable, you'd use the braces (think of it as "escaping" the variablename).
There is one additional usage.
One may have variable names like var1, var2, var11, var12, var101, etc.
Regardless if this is desirable variable naming, it just may be.
Using brackets one can precisely determine what is to be used:
assignment of $var11 may be ambiguous, using ${var1}1 or ${var11} leaves no room for mistakes.