How to filter ProfileList with wildcards - powershell

I'm writing a script to help streamline the deletion of users profile list's on our Citrix servers. I cant figure out how to filter the ProfileImagePath by wildcards.
On our Windows 2008 servers in Regedit I can search and sort the profile list of users cia its Profileimagepath but this gets all the users, I cant seem to extend that filter to only bring back wildcard entries.
set-location | 'HKLM:\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\ProfileList' |% {Get-ItemProperty $_.pspath } | Select
profileImagePath=username*
What I am getting is the whole list of user profiles, or if I put the filter =username* entry I get an error, What I wanted to get was just the results of the wildcard, Ideally just the usernames I put in the wildcard are what I want returned.

The code you posted is utterly broken. Please go find a PowerShell tutorial before proceeding any further.
Set-Location does not produce output, and even if it did, piping cmdlet output into a string wouldn't do anything at all.
You must not wrap statements at arbitrary places like you do in your sample code.
The wrapped registry path string will preserve the embedded newline (thus not matching what you want it to match).
Putting the argument of the Select-Object statement on the next line without escaping the newline will run Select-Object without arguments and the arguments as a separate statement (which most likely will throw an error because the arguments aren't a valid statement by themselves).
= is an assignment operator in PowerShell. Comparison operators are -eq (equality), -like (wildcard matches), and -match (regular expression matches).
You need Where-Object for filtering pipelined objects, not Select-Object. The latter is for selecting object properties.
You propbably meant to do something like this:
$username = '...'
$profilePath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
Get-ChildItem $profilePath | Where-Object {
(Get-ItemProperty $_.PSPath).ProfileImagePath -like "*${username}*"
}
or (simpler) like this:
$username = '...'
$profilePath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
Get-ChildItem $profilePath |
Get-ItemProperty |
Where-Object { $_.ProfileImagePath -like "*${username}*" }

Related

Script returning error: "Get-Content : An object at the specified path ... does not exist, or has been filtered by the -Include or -Exclude parameter

EDIT
I think I now know what the issue is - The copy numbers are not REALLY part of the filename. Therefore, when the array pulls it and then is used to get the match info, the file as it is in the array does not exist, only the file name with no copy number.
I tried writing a rename script but the same issue exists... only the few files I manually renamed (so they don't contain copy numbers) were renamed (successfully) by the script. All others are shown not to exist.
How can I get around this? I really do not want to manually work with 23000+ files. I am drawing a blank..
HELP PLEASE
I am trying to narrow down a folder full of emails (copies) with the same name "SCADA Alert.eml", "SCADA Alert[1].eml"...[23110], based on contents. And delete the emails from the folder that meet specific content criteria.
When I run it I keep getting the error in the subject line above. It only sees the first file and the rest it says do not exist...
The script reads through the folder, creates an array of names (does this correctly).
Then creates an variable, $email, and assigns the content of that file. for each $filename in the array.
(this is where is breaks)
Then is should match the specific string I am looking for to the content of the $email var and return true or false. If true I want it to remove the email, $filename, from the folder.
Thus narrowing down the email I have to review.
Any help here would be greatly appreciated.
This is what I have so far... (Folder is in the root of C:)
$array = Get-ChildItem -name -Path $FolderToRead #| Get-Content | Tee C:\Users\baudet\desktop\TargetFile.txt
Foreach ($FileName in $array){
$FileName # Check File
$email = Get-Content $FolderToRead\$FileName
$email # Check Content
$ContainsString = "False" # Set Var
$ContainsString # Verify Var
$ContainsString = %{$email -match "SYS$,ROC"} # Look for String
$ContainsString # Verify result of match
#if ($ContainsString -eq "True") {
#Remove-Item $FolderToRead\$element
#}
}
Here's a PowerShell-idiomatic solution that also resolves your original problems:
Get-ChildItem -File -LiteralPath $FolderToRead | Where-Object {
(Get-Content -Raw -LiteralPath $_.FullName) -match 'SYS\$,ROC'
} | Remove-Item -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
Note how the $ character in the RHS regex of the -match operator is \-escaped in order to use it verbatim (rather than as metacharacter $, the end-of-input anchor).
Also, given that $ is also used in PowerShell's string interpolation, it's better to use '...' strings (single-quoted, verbatim strings) to represent regexes, assuming no actual up-front string expansion is needed before the regex engine sees the resulting string - see this answer for more information.
As for what you tried:
The error message stemmed from the fact that Get-Content $FolderToRead\$FileName binds the file-name argument, $FolderToRead\$FileName, implicitly (positionally) to Get-Content's -Path parameter, which expects PowerShell wildcard patterns.
Since your file names literally contain [ and ] characters, they are misinterpreted by the (implied) -Path parameter, which can be avoided by using the -LiteralPath parameter instead (which must be specified explicitly, as a named argument).
%{$email -match "SYS$,ROC"} is unnecessarily wrapped in a ForEach-Object call (% is a built-in alias); while that doesn't do any harm in this case, it adds unnecessary overhead;
$email -match "SYS$,ROC" is enough, though it needs to be corrected to
$email -match 'SYS\$,ROC', as explained above.
[System.IO.Directory]::EnumerateFiles($Folder) |
Where-Object {$true -eq [System.IO.File]::ReadAllText($_, [System.Text.Encoding]::UTF8).Contains('SYS$,ROC') } |
ForEach-Object {
Write-Host "Removing $($_)"
#[System.IO.File]::Delete($_)
}
Your mistakes:
%{$email -match "SYS$,ROC"} - What % is intended to be? This is ForEach-Object alias.
%{$email -match "SYS$,ROC"} - Why use -match? This is much slower than -like or String.Contains()
%{$email -match "SYS$,ROC"} - When using $ inside double quotes, you should escape this using single backtick symbol (I have `$100). Otherwise, everything after $ is variable name: Hello, $username; I's $($weather.ToString()) today!
Write debug output in a right way: use Write-Debug, Write-Verbose, Write-Host, Write-Warning, Write-Error, Write-Information.
Can be better:
Avoid using Get-ChildItem, because Get-ChildItem returns files with attributes (like mtime, atime, ctime, etc). This additional info is additional request per file. When you need only list of files, use native .Net EnumerateFiles from System.IO.Directory. This is significant performace boost on huge amounts of files.
Use RealAllText or ReadAllLines or ReadAllBytes from System.IO.File static class to be more concrete instead of using universal Get-Content.
Use pipelines ;-)

Using Get-ChildItem with a string variable

I'm using Get-ChildItem to save file locations that match user-input parameters. I can't seem to figure out (I'm new to powershell) how to use string variables with cmdlets.
I'll define $search_param as it is in my script below.
I've tried using the built in debugger on powershell ise, and if anything it's confused me even more.
This is where I found that replacing the use of '$search_param' with the actual value of $search_param produced a csv output, but using '$search_param' gives no output. Some useful information might be that I defined $search_param with double quotations.
The line that is causing all the issues is:
Get-ChildItem 'C:\Users\USER\' -recurse '$search_param'
$search_param is defined as:
| where { $_.name -like "*peanut*" -or $_.name -like "*butter*" -or $_.name -like "*test*"}
| where { $_.extension -in ".txt",".csv",".docx" }
| where { $_.CreationTime -ge "08/07/1998" -and $_.CreationTime -le "08/07/2019" }
| select FullName | Export-Csv -Path .\SearchResults.csv -NoTypeInformation
Funnily enough I had it all working before I went to lunch and came back to a different story..
I'm using Export-csv in another piece of my script and that is working as intended.
I may have touched something small that isn't related to the line I provided as I think this should be working..
Per Technician's comment:
You can use invoke expression to "evaluate or run a specified string as a command and return the results of the expression or command."
Invoke-Expression "Get-ChildItem 'C:\Users\SGouldin\' -recurse $search_param"
From a little bit of research, relying on invoke expression isn't always the best practice.
In my case, I needed it because of the way I was attempting to handle user input (some weeeeird string concatenation/joins).

Powershell Script to Get Files with Certain Date in String

I'm building a powershell script to only grab files with a certain date in the string (yesterday's date) and display their names. But it doesn't seem to be going well. I've tried Googling but haven't found specifically help on what I'm trying to do:
$a = (Get-Date).AddDays(-1).ToString('yyyyMMdd')
$b = Get-ChildItem "E:\Export" -Filter {$_.Name -like '*'+$a.ToString()}
Get-ChildItem "E:\Export" -Filter *.txt |
Foreach-Object {
If ($b -like $a)
{
Write-Host $b
}
}
Any help would be appreciated.
$a IS already a string. You can't simply put a script block as a filter.
$a = (Get-Date).AddDays(-1).ToString('yyyyMMdd')
$b = Get-ChildItem "E:\Export" | Where-Object BaseName -like "*$a*"
$b
or
$b = Get-ChildItem "E:\Export\*$a*"
td;dr
$b = Get-ChildItem "E:\Export" -Filter ('*' + $a)
Or, using PowerShell's string expansion (interpolation):
$b = Get-ChildItem "E:\Export" -Filter "*$a"
-Filter parameter values:
are always [string]-typed
their specific syntax is provider-dependent
Since you're dealing with files, it is the FileSystem PS provider that interprets -Filter, and it expects a wildcard expression as an argument, as accepted by the underlying Windows API; the wildcard expression is implicitly matched against the file name.
Note:
Typically - such as in this case - such wildcard expressions work the same as PowerShell's own wildcard expressions, but the former have quirks in order to support legacy applications, while the latter offer additional features.
No standard provider accepts script blocks with arbitrary PowerShell code as a -Filter arguments, despite their widespread - but misguided - use with the Active Directory provider - see this answer.
To perform arbitrary filtering of output objects via script blocks in PowerShell code, pipe to the Where-Object cmdlet, as shown in LotPings' answer.
However, if feasible, use of -Filter should always be the first choice, because it filters at the source, meaning that the provider returns the already-filtered results to PowerShell (as opposed to having to filter the results after the fact, in PowerShell code), which can greatly speed up operations.

Using PowerShell how to I split the string of a selected property

I am very new to PowerShell and I can't seem to find a solution to the the question about splitting my selected properties value in powershell which works for me.
My current code without the split is:
((get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select IdentityReference
The purpose is to get a list of users who have access to a folder.
the results give me the domain and the user.
Domain\username
and I just want the username as it will be used in a further SQL query to look up details.
so I figured the best way to do it was to split the returned string on the '\' and take the 2nd value of the array it creates.
so far I am not getting any results back.
You can create custom results with Select-Object:
(get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select #{n='User'; e={$_.IdentityReference.Split('\')[1]}}
In PSv3+ you can take further advantage of member-access enumeration, combined with Split-Path -Leaf (which, as in WayneA's answer, is repurposed to extract the last \-separated component from a <realm>\<username> token):
(Get-Acl c:\temp).Access.IdentityReference | Split-Path -Leaf
Note that I've omitted the where {$_.IdentityReference -like '*\*'} filter, because - as far as I can tell - all .IdentifyReference match this pattern (with the first \-based token either being a domain name, a machine name, or a literal such as NT AUTHORITY or BUILTIN).
Also, this outputs an array of strings containing usernames only - whereas a Select-Object call without -ExpandProperty would output custom objects with the specified (possibly calculated) property/ies instead.
In PSv4+, you can make the command slightly more efficient by using the .ForEach() collection method:
(Get-Acl c:\temp).Access.IdentityReference.ForEach({ ($_ -split '\\')[-1] })
EBGreens answer is spot on, but I like to use split-path
You can do something like:
(get-Acl 'c:\temp').Access | where {$_.IdentityReference -like '*\*'} | Select #{name="UserName";Expression={$_.IdentityReference | split-path -leaf}}

Negate a -switch (all but the switch parameter)

How to obtain all the items except the one indicated by a switch?
I use:
Get-Disk -UniqueId 4875E7EB064AA60
to get information only a specific disk drive.
I want to use the same command, but get all drives except this one.
Something like this (in pseudo code):
Get-Disk -not( -UniqueId 4875E7EB064AA60 )
PowerShell as a language does not allow for "inverting" a parameter value. You need to filter the returned results with Where-Object after the fact.
Get-Disk | Where-Object { $_.UniqueId -ne '4875E7EB064AA60' }
There are several cmdlets that do allow expressing "everything but this" semantics, but it's up to the individual cmdlet if and how they implement that. For example:
The Get-ChildItem and Select-Object cmdlets have a parameter -Exclude that allows to exclude particular results.
The Select-String cmdlet has a switch -NotMatch to invert what is selected by the parameter -Pattern.
All cmdlets with filter parameters that allow expressing a not condition (like Get-WmiObject -Filter, Get-ADUser -Filter, or Get-ADUser -LDAPFilter) obviously also allow expressing a "not this" semantic.