Load Variables from text file and if key exists in hashtable, use key value - powershell

I'm trying to create a script, where the variables are outside of the PowerShell script. It's just a text file. In that file, we define server name, group names and their values. Group names meaning that the host has these strings in their hostname.
Hostnames have the higher priority, and Group names have the least priority. If neither is present, then there is a default value (inside the ps script).
The following elseif condition isn't working for me and I've tried a lot of other ways but could not get it working.
elseif ($MachineName.Contains("$var.key")) { # Doesn't work. What can be used here?
$foldername = ("$var.value") # Doesn't work. What can be used here?
}
Content of vars.txt:
#Groups
DB=folder7
#Hostnames
server_JIRA_001=folder3
server_DB_001=folder5
server_DB_005=folder6
If a hostname is server_DB_006 it will use value "folder7" as it has the "DB" in their hostname, but if the hostname is server_DB_005 then it will use value "folder6"
Content of script.ps1
$MachineName = "server_DB_006"
$var = (Get-Content "c:\Users\user\Desktop\vars.txt" -Raw | ConvertFrom-StringData)
if ($var.$MachineName -ne $null) {
# if hostname exists, then use its var.value
$foldername = ($var.$MachineName) # returns value of $var.hostname
} elseif ($MachineName.Contains("$var.key")) { # Doesn't work. What can be used here?
# if no hostname defined, then search for group name and use its var.value
$foldername = ("$var.value") # Doesn't work. What can be used here?
} else {
foldername = "default_folder"
}

So you can create a function who will bring back the key name if a string contains it. It loops through the keys and checks the $Text to see if it contains the key text else returns false.
function StringContainsHashTableKey(){
PARAM(
[string]$Text,
[hashtable]$HashTable
)
foreach($i in $Hashtable.Keys.GetEnumerator()){
if($Text -like "*$i*" ){
return $i
}
}
return $false
}
$Var = Get-Content "c:\Users\user\Desktop\vars.txt" -Raw | ConvertFrom-StringData
$MachineName = "server_DB_006"
$KeyMatch = StringContainsHashTableKey -Text $MachineName -HashTable $Var
if ($var.$MachineName -ne $null) { #if hostname exists, then use its var.value
$foldername = ($var.$MachineName) # returns value of $var.hostname
}
# If no hostname defined, then search for group name and use its var.value
Elseif ($KeyMatch -ne $false) { # Doesn't work. What can be used here?
$foldername = $var.$KeyMatch
}
Else {
$foldername = "default_folder"
}
$foldername

Related

Fast Registry Searcher in Powershell

I'm trying to incorporate this search-registry script that I found on Github into one of my scripts.
https://github.com/KurtDeGreeff/PlayPowershell/blob/master/Search-Registry.ps1
To test it, I used one of the examples that it provided:
Search-Registry -StartKey HKLM -Pattern $ENV:USERNAME -MatchData
After executing, I was still prompted to enter a StartKey and a Pattern into the console. After providing that information once again, the command fails.
cmdlet Search-Registry.ps1 at command pipeline position 1
Supply values for the following parameters:
StartKey: HKLM
Pattern: $ENV:USERNAME -MatchData
You must specify at least one of: -MatchKey -MatchValue -MatchData
At C:\Users\Cole\Desktop\Powershell\Search-Registry.ps1:93 char:5
+ throw "You must specify at least one of: -MatchKey -MatchValue -M ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (You must specif...alue -MatchData:String) [], RuntimeException
+ FullyQualifiedErrorId : You must specify at least one of: -MatchKey -MatchValue -MatchData
Is there something that I'm doing incorrectly?
I have tested that same function on GitHub too and also came across errors with it, so I decided to do a complete rewrite myself.
The function below can use either Regular Expression matches or Wildcard comparisons using the -like operator.
To search for nameless default properties, either don't specify the Pattern or RegexPattern parameters at all, or use one of them with an empty string.
The function also has a Recurse switch, so it is up to you if you want to recursively search through all subkeys or not.
Note that although the function performs fast, searching the registry can take a long time to finish..
# If you want to run this on PowerShell < 3.0 use
# New-Object -TypeName PSObject -Property #{ ... } wherever it says [PSCustomObject]#{ ... }
# and change the -version value for 'requires' to 2
#requires -version 3
function Search-Registry {
<#
.SYNOPSIS
Searches the registry on one or more computers for a specified text pattern.
.DESCRIPTION
Searches the registry on one or more computers for a specified text pattern.
Supports searching for any combination of key names, value names, and/or value data.
The search pattern is either a regular expression or a wildcard pattern using the 'like' operator.
(both are case-insensitive)
.PARAMETER ComputerName
(Required) Searches the registry on the specified computer(s). This parameter supports piped input.
.PARAMETER Pattern
(Optional) Searches using a wildcard pattern and the -like operator.
Mutually exclusive with parameter 'RegexPattern'
.PARAMETER RegexPattern
(Optional) Searches using a regular expression pattern.
Mutually exclusive with parameter 'Pattern'
.PARAMETER Hive
(Optional) The registry hive rootname.
Can be any of 'HKEY_CLASSES_ROOT','HKEY_CURRENT_CONFIG','HKEY_CURRENT_USER','HKEY_DYN_DATA','HKEY_LOCAL_MACHINE',
'HKEY_PERFORMANCE_DATA','HKEY_USERS','HKCR','HKCC','HKCU','HKDD','HKLM','HKPD','HKU'
If not specified, the hive must be part of the 'KeyPath' parameter.
.PARAMETER KeyPath
(Optional) Starts the search at the specified registry key. The key name contains only the subkey.
This parameter can be prefixed with the hive name.
In that case, parameter 'Hive' is ignored as it is then taken from the given path.
Examples:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall
HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall
Software\Microsoft\Windows\CurrentVersion\Uninstall
.PARAMETER MaximumResults
(Optional) Specifies the maximum number of results per computer searched.
A value <= 0 means will return the maximum number of possible matches (2147483647).
.PARAMETER SearchKeyName
(Optional) Searches for registry key names. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
.PARAMETER SearchPropertyName
(Optional) Searches for registry value names. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
.PARAMETER SearchPropertyValue
(Optional) Searches for registry value data. You must specify at least one of -SearchKeyName, -SearchPropertyName, or -SearchPropertyValue.
.PARAMETER Recurse
(Optional) If set, the function will recurse the search through all subkeys found.
.OUTPUTS
PSCustomObjects with the following properties:
ComputerName The computer name where the search was executed
Hive The hive name used in Win32 format ("CurrentUser", "LocalMachine" etc)
HiveName The hive name used ("HKEY_CURRENT_USER", "HKEY_LOCAL_MACHINE" etc.)
HiveShortName The abbreviated hive name used ("HKCU", "HKLM" etc.)
Path The full registry path ("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall")
SubKey The subkey without the hive ("Software\Microsoft\Windows\CurrentVersion\Uninstall")
ItemType Informational: describes the type 'RegistryKey' or 'RegistryProperty'
DataType The .REG formatted datatype ("REG_SZ", "REG_EXPAND_SZ", "REG_DWORD" etc.). $null for ItemType 'RegistryKey'
ValueKind The Win32 datatype ("String", "ExpandString", "DWord" etc.). $null for ItemType 'RegistryKey'
PropertyName The name of the property. $null for ItemType 'RegistryKey'
PropertyValue The value of the registry property. $null for ItemType 'RegistryKey'
PropertyValueRaw The raw, unexpanded value of the registry property. $null for ItemType 'RegistryKey'
The difference between 'PropertyValue' and 'PropertyValueRaw' is that in 'PropertyValue' Environment names are expanded
('%SystemRoot%' in the data gets expanded to 'C:\Windows'), whereas in 'PropertyValueRaw' the data is returned as-is.
(Environment names return as '%SystemRoot%')
.EXAMPLE
Search-Registry -Hive HKLM -KeyPath SOFTWARE -Pattern $env:USERNAME -SearchPropertyValue -Recurse -Verbose
Searches HKEY_LOCAL_MACHINE on the local computer for registry values whose data contains the current user's name.
Searches like this can take a long time and you may see warning messages on registry keys you are not allowed to enter.
.EXAMPLE
Search-Registry -KeyPath 'HKEY_CURRENT_USER\Printers\Settings' -Pattern * -SearchPropertyName | Export-Csv -Path 'D:\printers.csv' -NoTypeInformation
or
Search-Registry -Hive HKEY_CURRENT_USER -KeyPath 'Printers\Settings' -Pattern * -SearchPropertyName | Export-Csv -Path 'D:\printers.csv' -NoTypeInformation
Searches HKEY_CURRENT_USER (HKCU) on the local computer for printer names and outputs it as a CSV file.
.EXAMPLE
Search-Registry -KeyPath 'HKLM:\SOFTWARE\Classes\Installer' -Pattern LastUsedSource -SearchPropertyName -Recurse
or
Search-Registry -Hive HKLM -KeyPath 'SOFTWARE\Classes\Installer' -Pattern LastUsedSource -SearchPropertyName -Recurse
Outputs the LastUsedSource registry entries on the current computer.
.EXAMPLE
Search-Registry -KeyPath 'HKCR\.odt' -RegexPattern '.*' -SearchKeyName -MaximumResults 10 -Verbose
or
Search-Registry -Hive HKCR -KeyPath '.odt' -RegexPattern '.*' -SearchKeyName -MaximumResults 10 -Verbose
Outputs at most ten matches if the specified key exists.
This command returns a result if the current computer has a program registered to open files with the .odt extension.
The pattern '.*' means match everything.
.EXAMPLE
Get-Content Computers.txt | Search-Registry -KeyPath "HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine" -Pattern '*' -SearchPropertyName | Export-Csv -Path 'D:\powershell.csv' -NoTypeInformation
Searches for any property name in the registry on each computer listed in the file Computers.txt starting at the specified subkey.
Output is sent to the specified CSV file.
.EXAMPLE
Search-Registry -KeyPath 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace' -SearchPropertyName -Recurse -Verbose
Searches for the default (nameless) properties in the specified registry key.
#>
[CmdletBinding(DefaultParameterSetName = 'ByWildCard')]
Param(
[Parameter(ValueFromPipeline = $true, Mandatory = $false, Position = 0)]
[string[]]$ComputerName = $env:COMPUTERNAME,
[Parameter(Mandatory = $false, ParameterSetName = 'ByRegex')]
[string]$RegexPattern,
[Parameter(Mandatory = $false, ParameterSetName = 'ByWildCard')]
[string]$Pattern,
[Parameter(Mandatory = $false)]
[ValidateSet('HKEY_CLASSES_ROOT','HKEY_CURRENT_CONFIG','HKEY_CURRENT_USER','HKEY_DYN_DATA','HKEY_LOCAL_MACHINE',
'HKEY_PERFORMANCE_DATA','HKEY_USERS','HKCR','HKCC','HKCU','HKDD','HKLM','HKPD','HKU')]
[string]$Hive,
[string]$KeyPath,
[int32] $MaximumResults = [int32]::MaxValue,
[switch]$SearchKeyName,
[switch]$SearchPropertyName,
[switch]$SearchPropertyValue,
[switch]$Recurse
)
Begin {
# detect if the function is called using the pipeline or not
# see: https://communary.net/2015/01/12/quick-tip-determine-if-input-comes-from-the-pipeline-or-not/
# and: https://www.petri.com/unraveling-mystery-myinvocation
[bool]$isPipeLine = $MyInvocation.ExpectingInput
# sanitize given parameters
if ([string]::IsNullOrWhiteSpace($ComputerName) -or $ComputerName -eq '.') { $ComputerName = $env:COMPUTERNAME }
# parse the give KeyPath
if ($KeyPath -match '^(HK(?:CR|CU|LM|U|PD|CC|DD)|HKEY_[A-Z_]+)[:\\]?') {
$Hive = $matches[1]
# remove HKLM, HKEY_CURRENT_USER etc. from the path
$KeyPath = $KeyPath.Split("\", 2)[1]
}
switch($Hive) {
{ #('HKCC', 'HKEY_CURRENT_CONFIG') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::CurrentConfig; break }
{ #('HKCR', 'HKEY_CLASSES_ROOT') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::ClassesRoot; break }
{ #('HKCU', 'HKEY_CURRENT_USER') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::CurrentUser; break }
{ #('HKDD', 'HKEY_DYN_DATA') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::DynData; break }
{ #('HKLM', 'HKEY_LOCAL_MACHINE') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::LocalMachine; break }
{ #('HKPD', 'HKEY_PERFORMANCE_DATA') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::PerformanceData; break }
{ #('HKU', 'HKEY_USERS') -contains $_ } { $objHive = [Microsoft.Win32.RegistryHive]::Users; break }
}
# critical: Hive could not be determined
if (!$objHive) {
Throw "Parameter 'Hive' not specified or could not be parsed from the 'KeyPath' parameter."
}
# critical: no search criteria given
if (-not ($SearchKeyName -or $SearchPropertyName -or $SearchPropertyValue)) {
Throw "You must specify at least one of these parameters: 'SearchKeyName', 'SearchPropertyName' or 'SearchPropertyValue'"
}
# no patterns given will only work for SearchPropertyName and SearchPropertyValue
if ([string]::IsNullOrEmpty($RegexPattern) -and [string]::IsNullOrEmpty($Pattern)) {
if ($SearchKeyName) {
Write-Warning "Both parameters 'RegexPattern' and 'Pattern' are emtpy strings. Searching for KeyNames will not yield results."
}
}
# create two variables for output purposes
switch ($objHive.ToString()) {
'CurrentConfig' { $hiveShort = 'HKCC'; $hiveName = 'HKEY_CURRENT_CONFIG' }
'ClassesRoot' { $hiveShort = 'HKCR'; $hiveName = 'HKEY_CLASSES_ROOT' }
'CurrentUser' { $hiveShort = 'HKCU'; $hiveName = 'HKEY_CURRENT_USER' }
'DynData' { $hiveShort = 'HKDD'; $hiveName = 'HKEY_DYN_DATA' }
'LocalMachine' { $hiveShort = 'HKLM'; $hiveName = 'HKEY_LOCAL_MACHINE' }
'PerformanceData' { $hiveShort = 'HKPD'; $hiveName = 'HKEY_PERFORMANCE_DATA' }
'Users' { $hiveShort = 'HKU' ; $hiveName = 'HKEY_USERS' }
}
if ($MaximumResults -le 0) { $MaximumResults = [int32]::MaxValue }
$script:resultCount = 0
[bool]$useRegEx = ($PSCmdlet.ParameterSetName -eq 'ByRegex')
# -------------------------------------------------------------------------------------
# Nested helper function to (recursively) search the registry
# -------------------------------------------------------------------------------------
function _RegSearch([Microsoft.Win32.RegistryKey]$objRootKey, [string]$regPath, [string]$computer) {
try {
if ([string]::IsNullOrWhiteSpace($regPath)) {
$objSubKey = $objRootKey
}
else {
$regPath = $regPath.TrimStart("\")
$objSubKey = $objRootKey.OpenSubKey($regPath, $false) # $false --> ReadOnly
}
}
catch {
Write-Warning ("Error opening $($objRootKey.Name)\$regPath" + "`r`n " + $_.Exception.Message)
return
}
$subKeys = $objSubKey.GetSubKeyNames()
# Search for Keyname
if ($SearchKeyName) {
foreach ($keyName in $subKeys) {
if ($script:resultCount -lt $MaximumResults) {
if ($useRegEx) { $isMatch = ($keyName -match $RegexPattern) }
else { $isMatch = ($keyName -like $Pattern) }
if ($isMatch) {
# for PowerShell < 3.0 use: New-Object -TypeName PSObject -Property #{ ... }
[PSCustomObject]#{
'ComputerName' = $computer
'Hive' = $objHive.ToString()
'HiveName' = $hiveName
'HiveShortName' = $hiveShort
'Path' = $objSubKey.Name
'SubKey' = "$regPath\$keyName".TrimStart("\")
'ItemType' = 'RegistryKey'
'DataType' = $null
'ValueKind' = $null
'PropertyName' = $null
'PropertyValue' = $null
'PropertyValueRaw' = $null
}
$script:resultCount++
}
}
}
}
# search for PropertyName and/or PropertyValue
if ($SearchPropertyName -or $SearchPropertyValue) {
foreach ($name in $objSubKey.GetValueNames()) {
if ($script:resultCount -lt $MaximumResults) {
$data = $objSubKey.GetValue($name)
$raw = $objSubKey.GetValue($name, '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
if ($SearchPropertyName) {
if ($useRegEx) { $isMatch = ($name -match $RegexPattern) }
else { $isMatch = ($name -like $Pattern) }
}
else {
if ($useRegEx) { $isMatch = ($data -match $RegexPattern -or $raw -match $RegexPattern) }
else { $isMatch = ($data -like $Pattern -or $raw -like $Pattern) }
}
if ($isMatch) {
$kind = $objSubKey.GetValueKind($name).ToString()
switch ($kind) {
'Binary' { $dataType = 'REG_BINARY'; break }
'DWord' { $dataType = 'REG_DWORD'; break }
'ExpandString' { $dataType = 'REG_EXPAND_SZ'; break }
'MultiString' { $dataType = 'REG_MULTI_SZ'; break }
'QWord' { $dataType = 'REG_QWORD'; break }
'String' { $dataType = 'REG_SZ'; break }
default { $dataType = 'REG_NONE'; break }
}
# for PowerShell < 3.0 use: New-Object -TypeName PSObject -Property #{ ... }
[PSCustomObject]#{
'ComputerName' = $computer
'Hive' = $objHive.ToString()
'HiveName' = $hiveName
'HiveShortName' = $hiveShort
'Path' = $objSubKey.Name
'SubKey' = $regPath.TrimStart("\")
'ItemType' = 'RegistryProperty'
'DataType' = $dataType
'ValueKind' = $kind
'PropertyName' = if ([string]::IsNullOrEmpty($name)) { '(Default)' } else { $name }
'PropertyValue' = $data
'PropertyValueRaw' = $raw
}
$script:resultCount++
}
}
}
}
# recurse through all subkeys
if ($Recurse) {
foreach ($keyName in $subKeys) {
if ($script:resultCount -lt $MaximumResults) {
$newPath = "$regPath\$keyName"
_RegSearch $objRootKey $newPath $computer
}
}
}
# close opened subkey
if (($objSubKey) -and $objSubKey.Name -ne $objRootKey.Name) { $objSubKey.Close() }
}
}
Process{
if ($isPipeLine) { $ComputerName = #($_) }
$ComputerName | ForEach-Object {
Write-Verbose "Searching the registry on computer '$ComputerName'.."
try {
$rootKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($objHive, $_)
_RegSearch $rootKey $KeyPath $_
}
catch {
Write-Error "$($_.Exception.Message)"
}
finally {
if ($rootKey) { $rootKey.Close() }
}
}
Write-Verbose "All Done searching the registry. Found $($script:resultCount) results."
}
}
It returns a collection of objects with the following properties:
ComputerName : MYMACHINE
Hive : LocalMachine
HiveName : HKEY_LOCAL_MACHINE
HiveShortName : HKLM
Path : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{F3F5824C-AD58-4728-AF59-A1EBE3392799}
SubKey : SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{F3F5824C-AD58-4728-AF59-A1EBE3392799}
ItemType : RegistryProperty
DataType : REG_SZ
ValueKind : String
PropertyName : (Default)
PropertyValue : Sticky Notes Namespace Extension for Windows Desktop Search
PropertyValueRaw : Sticky Notes Namespace Extension for Windows Desktop Search
The difference between PropertyValue and PropertyValueRaw is that in PropertyValue Environment names are expanded
('%SystemRoot%' in the data gets expanded to 'C:\Windows'), whereas in PropertyValueRaw the data is returned as-is.
(Environment names return as '%SystemRoot%')
I suspect it may have to do with the way you downloaded the file, and how you're calling it. Please try the following steps and see if that works for you.
Click the link to view the file in RAW format, or just click here
Right-click on the page and Save As... a ps1 file in your scripts folder. Note: Make sure you save it with the .ps1 extension and not as txt.
Open Powershell, browse to the directory, and call it as so...
.\Search-Registry.ps1 -StartKey HKCU -Pattern "Visual Studio" -MatchData
You can modify however you like, of course, I chose to look for Visual Studio in HKCU since I knew it would be a quick search.

Concatenating a variable and a string literal without a space to an array using PowerShell

I'm trying add to a variable and a string in an array dynamically but i'm not getting expected output.
(1) I'm getting env name
(2) Concatinating the string and variable in an array
Code is as follows.
$env = $env:COMPUTERNAME.Substring(0,2)
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
$serverCount = $servers -split(",") | measure | % { $_.Count }
For ($i=0; $i -lt $serverCount; $i++)
{
$ServerName = $servers -split(',') -replace '\[\d+\]'
$server = $ServerName[$i]
Write-Host $server
}
output i'm getting as
$env+"server1.test.com"
$env+"server2.test.com"
Values are not getting concatenated properly and variable value is not getting displayed. Any help.
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
This is a scriptblock, not an array. {} is like a function, you have to run it for it to do anything (such as evaluating $env).
When you force it into a string using -split(",") what you get is text representation of the source code in the scriptblock, including the variable names.
As #Olaf comments, the right way to create an array of names is
$servers = ($env + "server1.test.com"), ($env + "server2.test.com")
This might be how I'd write it:
$env = $env:COMPUTERNAME.Substring(0,2)
"server1.test.com", "server2.test.com" | foreach-object {
"$env$_" -replace '\d+'
}

Reading strings from text files using switch -regex returns null element

Question:
The intention of my script is to filter out the name and phone number from both text files and add them into a hash table with the name being the key and the phone number being the value.
The problem I am facing is
$name = $_.Current is returning $null, as a result of which my hash is not getting populated.
Can someone tell me what the issue is?
Contents of File1.txt:
Lori
234 east 2nd street
Raleigh nc 12345
9199617621
lori#hotmail.com
=================
Contents of File2.txt:
Robert
2531 10th Avenue
Seattle WA 93413
2068869421
robert#hotmail.com
Sample Code:
$hash = #{}
Switch -regex (Get-content -Path C:\Users\svats\Desktop\Fil*.txt)
{
'^[a-z]+$' { $name = $_.current}
'^\d{10}' {
$phone = $_.current
$hash.Add($name,$phone)
$name=$phone=$null
}
default
{
write-host "Nothing matched"
}
}
$hash
Remove the current property reference from $_:
$hash = #{}
Switch -regex (Get-content -Path C:\Users\svats\Desktop\Fil*.txt)
{
'^[a-z]+$' {
$name = $_
}
'^\d{10}' {
$phone = $_
$hash.Add($name, $phone)
$name = $phone = $null
}
default {
Write-Host "Nothing matched"
}
}
$hash
Mathias R. Jessen's helpful answer explains your problem and offers an effective solution:
it is automatic variable $_ / $PSItem itself that contains the current input object (whatever its type is - what properties $_ / $PSItem has therefore depends on the input object's specific type).
Aside from that, there's potential for making the code both less verbose and more efficient:
# Initialize the output hashtable.
$hash = #{}
# Create the regex that will be used on each input file's content.
# (?...) sets options: i ... case-insensitive; m ... ^ and $ match
# the beginning and end of every *line*.
$re = [regex] '(?im)^([a-z]+|\d{10})$'
# Loop over each input file's content (as a whole, thanks to -Raw).
Get-Content -Raw File*.txt | foreach {
# Look for name and phone number.
$matchColl = $re.Matches($_)
if ($matchColl.Count -eq 2) { # Both found, add hashtable entry.
$hash.Add($matchColl.Value[0], $matchColl.Value[1])
} else {
Write-Host "Nothing matched."
}
}
# Output the resulting hashtable.
$hash
A note on the construction of the .NET [System.Text.RegularExpressions.Regex] object (or [regex] for short), [regex] '(?im)^([a-z]+|\d{10})$':
Embedding matching options IgnoreCase and Multiline as inline options i and m directly in the regex string ((?im) is convenient, in that it allows using simple cast syntax ([regex] ...) to construct the regular-expression .NET object.
However, this syntax may be obscure and, furthermore, not all matching options are available in inline form, so here's the more verbose, but easier-to-read equivalent:
$re = New-Object regex -ArgumentList '^([a-z]+|\d{10})$', 'IgnoreCase, Multiline'
Note that the two options must be specified comma-separated, as a single string, which PowerShell translates into the bit-OR-ed values of the corresponding enumeration values.
other solution, use convertfrom-string
$template=#'
{name*:Lori}
{street:234 east 2nd street}
{city:Raleigh nc 12345}
{phone:9199617621}
{mail:lori#hotmail.com}
{name*:Robert}
{street:2531 10th Avenue}
{city:Seattle WA 93413}
{phone:2068869421}
{mail:robert#hotmail.com}
{name*:Robert}
{street:2531 Avenue}
{city:Seattle WA 93413}
{phone:2068869421}
{mail:robert#hotmail.com}
'#
Get-Content -Path "c:\temp\file*.txt" | ConvertFrom-String -TemplateContent $template | select name, phone

How can I pass dynamic parameters to powershell script and iterate over the list?

I want to create a powershell script that accepts dynamic parameters and I also want to iterate through them.
eg:
I call the powershell script in the following manner.
ParametersTest.ps1 -param1 value1 -param2 value2 -param3 value3
And I should be able to access my params inside the script as follows:
for($key in DynamicParams) {
$paramValue = DynamicParams[$key];
}
Is there anyway to do this in powershell? Thanks in advance.
There is nothing built-in like that (essentially you're asking for PowerShell parameter parsing in the absence of any definition of those parameters). You can emulate it, though. With $args you can get at all arguments of the function as an array. You can then iterate that and decompose it into names and values:
$DynamicParams = #{}
switch -Regex ($args) {
'^-' {
# Parameter name
if ($name) {
$DynamicParams[$name] = $value
$name = $value = $null
}
$name = $_ -replace '^-'
}
'^[^-]' {
# Value
$value = $_
}
}
if ($name) {
$DynamicParams[$name] = $value
$name = $value = $null
}
To iterate over dynamic parameters you can either do something like you wrote
foreach ($key in $DynamicParams.Keys) {
$value = $DynamicParams[$key]
}
(note the foreach, not for, the latter of which cannot work like you wrote it) or just iterate normally over the hash table:
$DynamicParams.GetEnumerator() | ForEach-Object {
$name = $_.Key
$value = $_.Value
}

PowerShell cmdlet parameter value tab completion

How do you implement the parameter tab completion for PowerShell functions or cmdlets like Get-Service and Get-Process in PowerShell 3.0?
I realise ValidateSet works for a known list, but I want to generate the list on demand.
Adam Driscoll hints that it is possible for cmdlets but unfortunately hasn't elaborated.
Trevor Sullivan shows a technique for functions, but as I understand it, his code only generates the list at the time the function is defined.
I puzzled over this for a while, because I wanted to do the same thing. I put together something that I'm really happy with.
You can add ValidateSet attributes from a DynamicParam. Here's an example of where I've generated my ValidateSet on-the-fly from an xml file. See the "ValidateSetAttribute" in the following code:
function Foo() {
[CmdletBinding()]
Param ()
DynamicParam {
#
# The "modules" param
#
$modulesAttributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
# [parameter(mandatory=...,
# ...
# )]
$modulesParameterAttribute = new-object System.Management.Automation.ParameterAttribute
$modulesParameterAttribute.Mandatory = $true
$modulesParameterAttribute.HelpMessage = "Enter one or more module names, separated by commas"
$modulesAttributeCollection.Add($modulesParameterAttribute)
# [ValidateSet[(...)]
$moduleNames = #()
foreach($moduleXmlInfo in Select-Xml -Path "C:\Path\to\my\xmlFile.xml" -XPath "//enlistment[#name=""wp""]/module") {
$moduleNames += $moduleXmlInfo.Node.Attributes["name"].Value
}
$modulesValidateSetAttribute = New-Object -type System.Management.Automation.ValidateSetAttribute($moduleNames)
$modulesAttributeCollection.Add($modulesValidateSetAttribute)
# Remaining boilerplate
$modulesRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("modules", [String[]], $modulesAttributeCollection)
$paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("modules", $modulesRuntimeDefinedParam)
return $paramDictionary
}
process {
# Do stuff
}
}
With that, I can type
Foo -modules M<press tab>
and it will tab-complete "MarcusModule" if that module was in the XML file. Furthermore, I can edit the XML file and the tab-completion behavior will immediately change; you don't have to re-import the function.
Check the TabExpansionPlusPlus module on github, written by a former PowerShell team magician.
https://github.com/lzybkr/TabExpansionPlusPlus#readme
Classically, I used regex.
for example,
function TabExpansion {
param($line, $lastWord)
if ( $line -match '(-(\w+))\s+([^-]*$)' )
{
### Resolve Command name & parameter name
$_param = $matches[2] + '*'
$_opt = $Matches[3].Split(" ,")[-1] + '*'
$_base = $Matches[3].Substring(0,$Matches[3].Length-$Matches[3].Split(" ,")[-1].length)
$_cmdlet = [regex]::Split($line, '[|;=]')[-1]
if ($_cmdlet -match '\{([^\{\}]*)$')
{
$_cmdlet = $matches[1]
}
if ($_cmdlet -match '\(([^()]*)$')
{
$_cmdlet = $matches[1]
}
$_cmdlet = $_cmdlet.Trim().Split()[0]
$_cmdlet = #(Get-Command -type 'Cmdlet,Alias,Function,Filter,ExternalScript' $_cmdlet)[0]
while ($_cmdlet.CommandType -eq 'alias')
{
$_cmdlet = #(Get-Command -type 'Cmdlet,Alias,Function,Filter,ExternalScript' $_cmdlet.Definition)[0]
}
### Currently target is Get-Alias & "-Name" parameter
if ( "Get-Alias" -eq $_cmdlet.Name -and "Name" -like $_param )
{
Get-Alias -Name $_opt | % { $_.Name } | sort | % { $_base + ($_ -replace '\s','` ') }
break;
}
}
}
Reference
http://gallery.technet.microsoft.com/scriptcenter/005d8bc7-5163-4a25-ad0d-25cffa90faf5
Posh-git renames TabExpansion to TabExpansionBackup in GitTabExpansion.ps1.
And posh-git's redifined TabExpansion calls original TabExpansion(TabExpansionBackup) when completions don't match with git commands.
So all you have to do is redefine TabExpansionBackup.
(cat .\GitTabExpansion.ps1 | select -last 18)
============================== GitTabExpansion.ps1 ==============================
if (Test-Path Function:\TabExpansion) {
Rename-Item Function:\TabExpansion TabExpansionBackup
}
function TabExpansion($line, $lastWord) {
$lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
switch -regex ($lastBlock) {
# Execute git tab completion for all git-related commands
"^$(Get-AliasPattern git) (.*)" { GitTabExpansion $lastBlock }
"^$(Get-AliasPattern tgit) (.*)" { GitTabExpansion $lastBlock }
# Fall back on existing tab expansion
default { if (Test-Path Function:\TabExpansionBackup) { TabExpansionBackup $line $lastWord } }
}
}
===============================================================================
Redefine TabExpansionBackup(original TabExpansion)
function TabExpansionBackup {
...
### Resolve Command name & parameter name
...
### Currently target is Get-Alias & "-Name" parameter
...
}