In PowerShell how do I load multiple values into a variable? - string

I have a requirement at work to check several registry key values and I want to automate this process using PowerShell. One of the registry keys that I check has 3 values and I am not able to successfully check it using my PowerShell script.
I do not believe I am correctly loading the $value variable so that it can be compared to the value of $path.
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = “netlogon samr lsarpc”
if ($path -ne $value) {
Write-Host “Value is incorrect or missing.”
} else {
Write–Host “Config is correct.”
}
I expect the output to be
Config is correct.
Instead I get
Value is incorrect or missing.
Using Regedit I can see that the key has the correct values.

NullSessionPipe apparently is a REG_MULTI_SZ, meaning that the data is returned as an array of strings. You could do "$path" -ne $value to mangle the array into a flat string, but that would imply that the order of the substrings is identical in both strings. A better approach is to compare arrays via Compare-Object.
$path = (Get-ItemProperty HKLM:\...).NullSessionPipe
$value = 'netlogon', 'samr', 'lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}

#Ansgar, After some experimenting I was able to get the script to work. I deleted the blank spaces and added a carriage return after netlogon and samr. It works perfectly!!!
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = 'netlogon',
' samr',
' lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}

Related

How to compare strings that have an ampersand in them in PowerShell

I am using PowerShell to compare two strings that have an ampersand (&) in them (i.e. the string "Policies & Procedures").
No matter what I try, I cannot get these strings to match. I have tried trimmed the strings to get rid of an extra white spaces. I have tried wrapping the the string in both single and double quotes (and a combination of both):
"Policies & Procedures"
'Policies & Procedures'
"'Policies & Procedures'"
The code I am using to compare the strings is:
if ($term1 -eq $term2) {
do something
}
Inspecting the strings visually - they are identical, however the if statement never evaluates to true. Is there a way to compare these two strings so that it does evaluate to true?
EDIT
The context in which I am doing this string compare is looking for a term name in a taxonomy for a SharePoint site. Here is the code I am using:
function getTerm($termName) {
foreach($term in $global:termset.Terms) {
$termTrimmed = $term.Name.trim()
Write-Host "term name = $termTrimmed" -foregroundcolor cyan
if ($termTrimmed -eq $termName) {
return $term
}
}
return null
}
I have printed both term.Name and termName to the screen and they are identical. If there is no ampersand in the string, this function works. If there is an ampersand this function fails. This is how I know the ampersand is the problem.
This is a known quirk:
There are two types of ampersands that you need to be aware of when
playing with SharePoint Taxonomy
Our favorite and most loved
& ASCII Number: 38
And the impostor
& ASCII Number: 65286
After reading this article by Nick Hobbs, it became apparent
that when you create a term it replaces the 38 ampersand with a
65286 ampersand.
This then becomes a problem if you want to do a comparison with your
original source (spreadsheet, database, etc) as they are no longer the
same.
As detailed in Nick’s article, you can use the
TaxonomyItem.NormalizeName method to create a "Taxonomy" version of
your string for comparison:
Try this (not tested on real SharePoint):
function getTerm($termName)
{
foreach($term in $global:termset.Terms) {
$termNormalized = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($term.Name)
if ($termNormalized -eq $termName) {
return $term
}
}
return null
}
After converting both strings to char arrays and comparing the unicode value of the ampersands the problem is revealed. The ampersand used in the search string has a value of 38 while the ampersand returned from the SharePoint term store has a value of 65286 (called a full ampersand although looks identical to a regular ampersand on screen).
The solution was to write my own string comparison function and take into account the differences in the ampersand values. Here is the code:
function getTerm($termName) {
$searchChars = $termName.toCharArray()
$size = $searchChars.Count;
foreach($term in $global:termset.Terms) {
$match = $True
$chars = $term.Name.trim().toCharArray()
if ($size -eq $chars.Count) {
for ($i = 0; $i -lt $size; $i++) {
if ($searchChars[$i] -ne $chars[$i]) {
# handle the difference between a normal ampersand and a full width ampersand
$charCode1 = [int] $searchChars[$i]
$charCode2 = [int] $chars[$i]
if ((($charCode1 -eq 38) -or ($charCode1 -eq 65286 )) -and (($charCode2 -eq 38) -or ($charCode2 -eq 65286 ))) {
continue
} else {
$match = $False
break
}
}
}
} else {
$match = $False
}
if ($match -eq $True) {
return $term
}
}
return $null
}

Powershell Passing Variable to Function

I don't understand what is going on...
In the VerifyEmailSettings function, the $AdminEmailAddress is one of many parameters i can pass to the ps command i am using.
I want to be able to pass the paramater name, and value to other functions like below. However, when i pass this along, i get some odd results. As you can see in the results, trying to print the $SettingName in the VerifyEmailSettings function echos AdminEmailAddress admin#superuser.com Verified, Same instead of what i want... AdminEmailAddress Verified, Same The "admin#superuser.com is mixed in there somehow. Same happens with the $SetName in the SetEmailSettings functions.
Thanks in advance!!
Write-Host "Starting Script"
#Assigning Variables
$AdminEmailAddress = "admin#superuser.com"
$SmtpServer = "exchange.local"
$FromEmailAddress = "fsrm#omg.com"
If (GetInstallStatus){
Write-Host "FSRM Installed, Skipping Install"
Write-Host "Checking Email Settings"
VerifyEmailSettings([string]"AdminEmailAddress",[string]$AdminEmailAddress)
} else {
Write-Host "FSRM Not Installed, Installing"
Install-WindowsFeature –Name FS-Resource-Manager –IncludeManagementTools
If (GetInstallStatus){
Write-Host "FSRM Installed"
} else {
Write-Host "FSRM Error on Install, Halting"
#halt here
}
}
function GetInstallStatus {
$status = (Get-WindowsFeature -Name FS-Resource-Manager | ft Installed -autosize -hidetableheaders | out-string).trim();
return $status
}
function VerifyEmailSettings([string]$SettingName, [string]$SettingData) {
$Verify = (Get-FsrmSetting | Select-Object $SettingName | FT -autosize -hidetableheaders | Out-String).Trim()
If ($Verify -eq $SettingData) {
Write-Host $SettingName "Verified, Same"
SetEmailSettings([string]$SettingName, [string]$SettingData)
} Else {
Write-Host $SettingName "Wrong, Updating"
SetEmailSettings([string]$SettingName, [string]$SettingData)
}
}
function SetEmailSettings([string]$SetName, [string]$SetData) {
$SetName
#Set-FsrmSetting $SetName $SetData
}
Here is the results i get:
Starting Script
FSRM Installed, Skipping Install
Checking Email Settings
AdminEmailAddress admin#superuser.com Verified, Same
AdminEmailAddress admin#superuser.com
Do not call PowerShell functions with parentheses and commas
VerifyEmailSettings([string]"AdminEmailAddress",[string]$AdminEmailAddress)
What you're actually doing here is passing an array containing both values as the first argument and nothing for the second argument. That should be written like this:
VerifyEmailSettings "AdminEmailAddress" $AdminEmailAddress
Or
VerifyEmailSettings -SettingName "AdminEmailAddress" -SettingData $AdminEmailAddress
(there is no need to cast your strings as [string])
Use Strict Mode
What you've done is a common error in PowerShell, made more common by the fact that you do use parentheses and commas when calling methods on .Net objects. I still do this once in a while after years of using PowerShell.
You can set strict mode which actually catches this for you and warns you about it:
Set-StrictMode -Version 2.0

Using PowerShell to find the differences in strings

So I'm playing around with Compare-Object, and it works fine for comparing files. But what about just strings? Is there a way to find the difference between strings? CompareTo() is good about reporting that there is a difference, but not what the difference is. For example:
PS:> $a = "PowerShell rocks"
PS:> $b = "Powershell rocks"
PS:> $a.CompareTo($b)
1
PS:> Compare-Object -ReferenceObject $a -DifferenceObject $b
PS:>
Nothing returned.
Any way to let me know about the actual difference between the strings, not just that there is a difference?
Perhaps something like this:
function Compare-String {
param(
[String] $string1,
[String] $string2
)
if ( $string1 -ceq $string2 ) {
return -1
}
for ( $i = 0; $i -lt $string1.Length; $i++ ) {
if ( $string1[$i] -cne $string2[$i] ) {
return $i
}
}
return $string1.Length
}
The function returns -1 if the two strings are equal or the position of the first difference between the two strings. If you want case-insensitive comparisons, you would need to use -eq instead of -ceq and -ne instead of -cne.

How to duplicate objects in variable with different variable names for PowerShell

I would like to create a duplicate of same objects in different variable names.
The object I required is archive files from dotnetzip.
The following code is the full implementation:
[System.Reflection.Assembly]::LoadFrom($zipFileDirectory + "Ionic.Zip.dll")
$zipfile = [Ionic.Zip.ZipFile]::Read($zipfilename)
foreach ($file in $zipfile)
{
$strSearchItem = [string]$file.FileName
$strSearchItem = $strSearchItem.TrimEnd("/")
$newfile = $file.PSObject.Copy()
for ($i = 0; $i -lt $newfile.Count; $i++)
{
if ($strSearchItem -like $searchFolderName + "/*")
{
$newFile[$i].FileName = $newFile[$i].FileName.Replace($searchFolderName + "/", "")
$newFile[$i].Extract($fileDestination, [Ionic.Zip.ExtractExistingFileAction]::OverWriteSilently)
}
}
}
$zipfile.Dispose()
For this purpose I need to be able to copy $file as separate entity from $zipfile, or at least retain the original default value for $file (making it read-only doesnt seemed viable). Is there any workaround for this matter?
Thanks in advance.
maybe
$newFile = $zipfile.PSObject.Copy()
in reply to #bdrc comment
example adding a comment to the zip
PS>$zf=[ionic.zip.zipfile]::read("c:\temp\zip\test.zip")
PS>$zf.comment
PS>$zf2=$zf.psobject.copy()
PS>$zf2.comment="TEST COMMENT"
PS>$zf2.save("c:\temp\test2.zip")
when opening original file with 7-zip I dont see the comment, I can see it in the new zipfile ...

How can I check if an environmental variable is set?

I have the following code in perl
my %Opt =
(
boards_txt => "$ENV{'ARDUINO_DIR'}/hardware/arduino/boards.txt",
);
In this you can see that the env variable ARDUINO_DIR is append. Some users might not have this variable set. If that is the case, then I want to hardcode a path.
Question: How can I check if the env variable is set or not?
The correct answers have been given, but I wanted to add that you might make use of the rather handy defined-or assignment operator //=:
my $dir = $ENV{'ARDUINO_DIR'};
$dir //= "/other/path";
Or, as RobEarl points out in the comment:
my $dir = $ENV{'ARDUINO_DIR'} // "/other/path";
This is the logical equivalent of
my $dir;
if (defined $ENV{'ARDUINO_DIR'}) {
$dir = $ENV{'ARDUINO_DIR'};
} else {
$dir = "/other/path";
}
As mob points out, the defined-or operator requires perl v5.10. For those who still have not upgraded to that version it is also possible to use the || operator:
my $dir = $ENV{'ARDUINO_DIR'} || "/other/path";
The caveat being that this will overwrite values that are interpreted as false, which may in some context be considered proper values, such as the empty string or zero. In this case, however, it is unlikely that 0 or the empty string are valid paths.
You are already using the %ENV hash. It contains all environment variables, so you could do something like:
if (defined $ENV{'ARDUINO_DIR'}) { $prefix = $ENV{'ARDUINO_DIR'} }
else { $prefix = '/path/to/arduino/dir/' }
my $path_to_txt = $prefix . 'boards.txt';
I suggest you use File::Spec for working with paths.
You can check for existence of a hash key with exists:
perl -le 'print "fnord!" if exists $ENV{"ARDUINO_DIR"}'

Resources