Populating an array using inline variables - powershell

I generally use the following format when I populate a script with variables:
$string = ("Hi my name is {0} and I live in {1}, {2}." -f $username,$usercity,$userstate)
However this doesn't seem to work with an array, or I might be getting the syntax completely wrong:
$Arguments = #("/Settings:{0}", "/Tests:{1}", "/output:{2}" -f $TestSetting,$TestList,$Output)
When I output the results of that association, it all comes out as one string (with the substitution correct). If I look at the Count, it is 1. What am I missing?

Each value in the array needs to be it's own expression with it's own operator:
$Arguments = #(("/Settings:{0}" -f $TestSetting), ("/Tests:{0}" -f $TestList), ("/output:{0}" -f $Output))

Related

Can Powershell return complete interpolated values using convertfrom-stringdata

I have a .properties file with the following properties in them:
repository.host=hostname.com/nexus
repository.api.url=https://${repository.host}/service/rest/v1
repository.url=https://${repository.host}/repository
I am able to return the values using the following powershell function:
static [string] getProperty( [string] $property ){
$properties = "../resources/vars/$([jenkins]::PROPERTIES_FILE)"
$properties = get-content $properties | convertfrom-stringdata
return $properties.$property
}
When attempting to return the property repository.url powershell return this string: https://${repository.host}/repository/
My question is: Is it possible through features that already exist in powershell for the returned string to be https://hostname.com/nexus/repository/?
By design, for security reasons, ConvertFrom-StringData does not perform string expansion (interpolation) on its input.
Assuming you trust the input string[1], you can perform the expansion on demand, after having read the values from the file.
Note that use of ConvertFrom-StringData is problematic, as you've discovered, because the hashtable it creates invariably has unordered keys; that is, the order of the entries does not reflect the order in which the properties are defined in the file.
Therefore, processing the hashtable entries can make the on-demand expansion fail, if an out-of-order entry is processed before another entry whose value it needs for the expansion.
The solution is to roll your own ConvertFrom-StringData variant[2] that reads the properties into an ordered hashtable.
This additionally allows you to combine the read-from-file and expansion-on-demand tasks:
# Create a sample properties file.
#'
repository.host=hostname.com/nexus
repository.api.url=https://${repository.host}/service/rest/v1
repository.url=https://${repository.host}/repository
'# > sample.properties
# Parse the file and build an *ordered* hashtable from it.
$orderedHash = [ordered] #{}
switch -Regex -File sample.properties {
'^\s*#|^\s*$' { continue } # skip comment and blank lines.
default {
# Determine the key and value...
$key, $value = $_ -split '=', 2
# ... and create the entry while expanding ${...} references to previous
# entries.
$orderedHash[$key.Trim()] = $ExecutionContext.InvokeCommand.ExpandString((
$value.Trim() -replace '\$\{([^}]+)\}', '$$($$orderedHash[''$1''])'
))
}
}
# Output the result.
$orderedHash
Note the use of method $ExecutionContext.InvokeCommand.ExpandString to perform on-demand string expansion (interpolation); since this method isn't easy to discover, GitHub issue #11693 proposes that this functionality be surfaced as a proper, easily discoverable cmdlet named something like Expand-String or Expand-Template.
Note: In order to be able to use $ExecutionContext from the method of a PS custom class, you must explicitly reference it in the global scope via $global:ExecutionContext.
For more information about the regex-based -replace operator, see this answer.
The above yields (note that the input order was maintained):
Name Value
---- -----
repository.host hostname.com/nexus
repository.api.url https://hostname.com/nexus/service/rest/v1
repository.url https://hostname.com/nexus/repository
[1] Via $(), the subexpression operator, it is possible to embed arbitrary commands in the input strings.
[2] The code below does not replicate all features of ConvertFrom-String data, but it works with the sample input. While it does support skipping comment lines (those whose first non-whitespace character is a #) and blank lines, treating \ as escape characters and supporting escape sequences such as \n for a newline is not implemented.
The original solution provided by #mklement0 was very useful, and has guided me towards a more complete solution. This solution accomplishes/corrects a couple of things:
The ability to create the hashtable from a file source.
The ability to access the $ExecutionContext variable from within a class method, using the $global: scope.
The ability to thoroughly parse all keys within the hashtable.
static [string] getProperties ( [string] $file, [string] $property ){
$properties = get-content $file -raw | convertfrom-stringdata
while ( $properties.values -match '\$\{([^}]+)\}' ){
foreach ($key in #($properties.Keys)) {
$properties[$key] = $global:ExecutionContext.InvokeCommand.ExpandString( ($properties[$key] -replace '\$\{([^}]+)\}', '$$($$properties[''$1''])') )
}
}
return $properties[$property]
}
Note: When the while loop is not present and searching matches of ${*}, any given returned value may not be completely interpolated or expanded. As an example without the while loop present output from a file may look like this:
/nexus
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
443
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
hostname.com
https
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
And if you were to run the same script again (still without the while loop) would look like this:
hostname.com
https://hostname.com:443/nexus
/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
https://${nexus.hostname}:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
https://hostname.com:443/nexus/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
443
https
https://${nexus.hostname}:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
The reason for the sometimes incompletely interpolated/expanded strings is because hashtables are naturally unordered. With the introduction of the while loop, results will not be returned until all interpolated/expanded strings are resolved.
The official output would look as such:
hostname.com
https://hostname.com:443/nexus
/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
https://hostname.com:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
https://hostname.com:443/nexus/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
443
https
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip

How to cut specific string?

I have a string with different length. I want to cut a specific word in my string.
Please help, I am new to PowerShell.
I tried this code, it's still not what I need.
$String = "C:\Users\XX\Documents\Data.txt"
$Cut = $String.Substring(22,0)
$Cut
My expectation is that I can return the word Data.
Assuming the string is always the same format (i.e. a path ending in a filename), then there are quite a few ways to do this, such as using regular expressions. Here is a slightly less conventional method:
# Define the path
$filepath = "C:\Users\XX\Documents\Data.txt"
# Create a dummy fileinfo object
$fileInfo = [System.IO.FileInfo]$filePath
# Get the file name property
$fileInfo.BaseName
Of course, you could do all of this in one step:
([System.IO.FileInfo]"C:\Users\XX\Documents\Data.txt").BaseName
If the path is an existing one, you could use
(Get-Item $String).BaseName
Otherwise
(Split-Path $String -Leaf) -Replace '\.[^\.]*$'
While in that specific example the simplest way is to use Substring(startPosition,length) to extract file name you'd probably want to use something like this:
(("C:\Users\XX\Documents\Data.txt".split("\\"))[-1].Split("."))[0]
Explanation:
("C:\Users\XX\Documents\Data.txt".split("\\"))[-1]
that part split the path by \ and returns last item (escaping it seems to be not mandatory by the way so you can use .split("\") instead of .split("\\")). From it you receive Data.txt so you have to separate name and extension. You can do this by splitting by . and choosing first element returned
There are number of ways of doing it depending upon your input -
Method 1 - Hard-coding using the sub-string function.
$String = "C:\Users\XX\Documents\Data.txt"
$Cut = $String.Substring(22,4)
$Cut
The above approach will work for a single input but will become difficult to manage for multiple inputs of different lengths.
Method 2 - Using the split method
$String = "C:\Users\XX\Documents\Data.txt"
$cut = $String.Split("\")[-1].split(".")[0]
$cut
Split method will split string into substring. The index [-1] will return the last value returned by the split method.
The second split is to return the word Data from the word Data.txt.
Method 3 - If the input is a file path
$string = Get-ChildItem $env:USERPROFILE\Desktop -File | select -First 1
$Cut = $String.BaseName
More about method 3 here.
If you can use Powershell 6 - SplitPath
#Requires -Version 6.0
Split-Path $String -LeafBase

list of reserved variables in mib2c

Where can I find the list of reserved variable names for mib2c "language"? I mean the possible variables that are not described here, like ${name}, which evaluates to the OID name that is passed as the argument to mib2c.c.
Are there any other variables like that?
Is there one that carries the name of the .conf file that was passed to the mib2c.
Looking at local/mib2c from net-snmp-5.7.3, the pre-populated variables are populated by the following code:
$outputName = $mibnode->{'label'} if (!defined($outputName));
$outputName =~ s/-/_/g;
$vars{'name'} = $outputName;
$vars{'oid'} = $oid;
$vars{'example_start'} = " /*\n" .
" ***************************************************\n" .
" *** START EXAMPLE CODE ***\n" .
" ***---------------------------------------------***/";
$vars{'example_end'} = " /*\n" .
" ***---------------------------------------------***\n" .
" *** END EXAMPLE CODE ***\n" .
" ***************************************************/";
So, you end up with the following pre-populated variables:
$name is the "output prefix" specified using the -f option (or $mibnode->{'label'}, whatever that is, if the -f option wasn't used), with dashes substituted with underscores.
$oid is the value of mib2c's argument (called "mibNode" in the usage help).
$example_start and $example_end are hard-coded strings.
That's it.
To create $config with the value of the -c argument (or mib2c.conf if the -c option wasn't used), you could alter mib2c to add the following to the assignments shown above:
$vars{config} = $configfile;
Alternatively, I believe the following will also create $config, but the value passed to the -c option will be prepended with a directory name:
#perleval $vars{config} = $configfile; 0#
You could try to obtain the original value with the following (which assumes the original value didn't contain a /);
#perleval $vars{config} = $configfile =~ m{([^/]+)\z}s ? $1 : undef; 0#
Completely untested. I don't know anything about SNMP or mib2c.

Not getting exact value in Variable becasue value contains $ char

I have a string "/Name Pa$Name#my"
$myname = GetContent("Name")
GetContent is only gets the key and gives the value for that Key. I cannot modify the GetContent function.
If i execute above, as "Pa$Name#my" contains the '$' char, $myname gives me value as "Pa#my" and ignores "$Name" content.
What can i do to get the $myname = Pa$Name#my ? Can i append some special characters while assigning the $myname variable.
As far as I understand your trouble comes from the fact that the var $name does not exist. You can use single qotes if you don't want Powershell to look for vars :
$a = 'Pa$Name#my'

Expand a function in a here-string in powershell

I need to use the result of a function in some output, and i would like to streamline the script by expanding the function into the here-strings of powershell
# instead of
$result = myfunction
"The result is $result"
# syntax that would let me call the function inside the string
"the result is ?myfunction()"
I don't see any documentation regarding this behavior but i'd really appreciate having missed it rather than it not existing.
If it is not doable what alternatives do i have?
"the result is also " myfunction
...?
Use a subexpression for that:
"the result is $(myfunction)"
Other options are concatenating string and function output:
"the result is " + (myfunction)
or using the format operator:
"the result is {0}" -f (myfunction)