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

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}}

Related

Powershell Folder List Filter

I have a folder and inside has a list of subfolders/files
Folders
2022
20221101
20221103
20221107
20221108
test123
results
test.txt
Using Powershell
How do get the list of folders that are dates.
How do I get the second latest folder (20221107).
This is what I was able to come with so far:
Get-ChildItem "C:\code\Test" -Filter "2022*" | Sort-Object Name -Descending
You can use TryParseExact method from DateTime to parse the folders names and avoid any type of error, this can be combined with Sort-Object and Select-Object to get the 2nd latest. I have added -Directory to output only folders. Also changed your filter to -Filter "2022????" to ensure you're matching folders that start with 2022 and are followed by 4 characters.
$ref = [ref] [datetime]::new(0)
Get-ChildItem "C:\code\Test" -Filter "2022????" -Directory | Sort-Object {
$result = [datetime]::TryParseExact(
$_.Name,
'yyyyMMdd',
[cultureinfo]::InvariantCulture,
[Globalization.DateTimeStyles]::AssumeLocal,
$ref
)
if($result) { $ref.Value }
} -Descending | Select-Object -Index 1
If you want to ensure you're matching folders with a date format (because -Filter may be too permissive), you can pipe to Where-Object for additional filtering via regex:
$ref = [ref] [datetime]::new(0)
Get-ChildItem "C:\code\Test" -Filter "2022????" -Directory |
Where-Object Name -Match '^2022\d{4}$' | Sort-Object {
# rest of the code here
} -Descending | Select-Object -Index 1
To provide an alternative to Santiago's helpful answer:
Since the timestamps that your folder names represent sort lexically in a way that is equivalent to their chronological sorting, you may not need to convert them to [datetime] instances, and can sort them as-is.
Get-Item C:\code\Test\* -Include 2022???? |
Sort-Object Name -Descending |
Select-Object -Index 1
Note the use of -Include instead of -Filter (which in turn necessitates ending the -Path argument with \* and using Get-Item instead of Get-ChildItem), because the -Filter parameter has legacy quirks that prevent character-exact matching with multiple ? wildcards - see this answer for background information.
Unfortunately, as this solution and the linked answer shows, making -Include (and -Exclude) work as intended is tricky as of PowerShell 7.2.x, and requires memorizing non-intuitive rules.
On the plus side, -Include, which (unlike -Filter) uses PowerShell's wildcard expressions, would also allow you to create a more specific pattern, such as -Include 2020[0-1][0-9][0-3][0-9] (which still isn't strict enough to rule out invalid digit combinations, however).

grep gci output in powershell

I am trying to determine if some environment variables are set (for postgres environment). They usually start with PG. (E.g. PGUSER, PGPASSWORD, etc). The following command does output it. (Provided I set it previously).
gci env:* | sort name | more
To eliminate the scrolling I tried the following:
gci env:* | sort name | select-string "PG"
This doesn't return anything. What am I doing wrong here?
Edit: the alternative I have for now:
gci env:* | sort name | % { $var = $_.Name + ":" + $_.Value; Write-Output $var } | select-string "PG"
There must be a better alternative.
You're using the wrong mindset. Don't try to work with PowerShell like everything is a string. That's Unix-like thinking, and it's going to work as well as driving nails with a screwdiver. You need to switch to object-oriented thinking because in PowerShell you're working with objects 99% of the time.
Generally, you would just do this for something as simple as what you're looking for:
Get-ChildItem Env:PG* | Sort-Object -Property Name
If the globbing that Get-ChildItem supports doesn't work, you would want to use Where-Object with the -like operator which is similar globbing to what Get-ChildItem can do:
Get-ChildItem Env:* | Where-Object Name -like 'PG*' | Sort-Object -Property Name
If you need to search values, you can do it like this:
Get-ChildItem Env:* | Where-Object Value -like 'PG*' | Sort-Object -Property Name
And if you want to do both, you'd use the full synax of Where-Object:
Get-ChildItem Env:* | Where-Object { $_.Name -like 'PG*' -or $_.Value -like 'PG*' } | Sort-Object -Property Name
Or you can use the -match operator, which lets you specify a .Net regular expression:
Get-ChildItem Env:* | Where-Object Name -match '^PG' | Sort-Object -Property Name
Or if you know exactly what you're looking for:
$Vars = 'PGUSER', 'PGPASSWORD'
Get-ChildItem Env:* | Where-Object Name -in $Vars | Sort-Object -Property Name
Remembering, of course, that PowerShell is usually case-insensitive. You can specify -clike, -cmatch, -cin, etc. if you want case-sensitive operators.
Alternately, you can use the $env: automatic variable namespace.
if ($null -eq $env:PGUSER) { 'Not set' }
See also Get-Help about_Environment_Variables.
Beware that setting environment variables permanently is not exactly self-evident. It's described briefly in the above link, but the bottom line is that you have to call [System.Environment]::SetEnvironmentVariable(), which you can find documented here. In Windows land, environment variables are basically legacy features with the exception of Windows OS level variables (like PATH) so they're no longer supported like you might expect.
Your approach to how this command should work and your instinct that there has to be a better alternative is exactly correct. This is quite a frustrating issue in my mind and I also asked a variation on this question a few days back.
Select-String only handles strings and what you are passing to it in the above is not a string, so it returns nothing. Obviously, you might think that since Select-String requires a string, that it would implicitly change it into a string, but no. So the next thing to consider is to change it to a string, but that creates even more confusion.
gci env:* | sort name | out-string | select-string "Pro"
So now you just get everything returned. What's happening here is that out-string returns all lines as a single string, so if there is any hit for "Pro" you get everything returned.
What you need to do is to use out-string -stream which splits the string up by linebreaks so that you get a string per line, and only then do you get rational output.
gci env:* | sort name | out-string -stream | select-string "Pro"
More on this here: Using PowerShell sls (Select-String) vs grep vs findstr. The github request linked to in there is trying to change the functionality so that select-string will implicitly have an out-string -stream in the background so that your original command will work.
Often we need strings to output results and there is nothing wrong with wanting to manipulate strings (in fact, it depends what you need of course - if you need to do further object manipulations, keep it as an object for that, but if you just need the string output, you should not have to jump through hoops to get that!). If you use a string-manipulation tool like select-string then PowerShell should at least convert the incoming information to a string to provide meaningful output. Compare with findstr: if you pipe the above to findstr.exe, exactly that will happen and it will implicitly convert with | out-string -stream for findstr (and all other external / non-PowerShell programs) and so gci env:* | findstr "Pro" (on a PowerShell console!) gives you rational output. select-string is a string-manipulation tool so I find the idea that people are not thinking right about it for expecting a string-manipulation tool to manipulate the incoming information as a string to be unfair on users. PowerShell is an incredibly versatile language but I think this is a common area of confusion. Hopefully, future versions of select-string will operate in the above fashion as a result of the change request on GitHub, but in the meantime, just use | out-string -stream and it will all work as expected, including for other string manipulations which you can then deal with easily:
(gci env:* | sort name | out-string -stream) -replace "Pro", "XXX" | select-String "XXX"
to keep this short: Your approach doesn't work in PowerShell. All you need to do is
# Short Version
gci env: | ? Name -match "PG" | sort Name
# Long Version
Get-ChildItem -Path env: |
Where-Object -FilterScript { $_.Name -match "PG" } |
Sort-Object -Property Name
Select-String works fine with string content piped one by one instead of a big stream.
Cheers

How to filter ProfileList with wildcards

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}*" }

Import-CSV and Foreach to use Get-ADUser

I have a CSV file that looks like this:
name
fname1lname1#companyemail.com
fname2lname2#companyemail.com
...
I would like to loop through each email address and query AD for that address to grab the user objects ID. I have been able to do this using a script, but I would like to be able to do it using just one line.
This is what I've done so far:
import-csv -path .\csv_file.csv | foreach-object { get-aduser -filter { proxyaddresses -like "*$_.name*} | select name } | out-file .\results.csv
This obviously doesn't work and I know it has something to do with how I am handling my $_ object in the foreach loop.
I'm hoping for the output to look something like:
fname1lname1#companyemail.com,userID1
fname2lname2#companyemail.com,userID2
...
You are filtering on the property proxyaddresses however that is not part of the default property set that Get-AdUser returns. Also your code had a errant " which might have been a copy paste error.
Import-CSV -Path .\csv_file.csv | ForEach-Object {
Get-ADUser -Filter "ProxyAddresses -like '*$($_.name)*'" -Properties ProxyAddresses,EmailAddress | select EmailAddress,SamAccountName
} | Export-CSV .\results.csv -NoTypeInformation
-Filter can be tricky sometimes as it is looking for string input. Wrap the whole thing in quotes and use a sub expression to ensure that the variable $_.Name is expanded properly and has is asterisks surrounding it.
Since you are also looking for emailaddress we add that to the properties list as well. I will assume the second column is for samaccountname.
We also use Export-CSV since that will make for nice CSV output.
If you're using Exchange this can be much simpler and faster if you use the Exchange cmdlets:
Import-CSV -Path .\csv_file.csv | ForEach-Object {
Get-Recipient $_ |
Select PrimarySMTPAddress,SamAccountName
} | Export-CSV .\results.csv -NoTypeInformation
Exchange requires all email address to be unique, and maintains it's own internal database that uses email address as a primary index so it can return the DN and SamAccountName that goes with that email address almost immediately.
AD doesn't require them to be unique, so it doesn't index on that attribute and it has to search every user object looking for the one that has that email address in it's proxy address collection.

Run a function on each element of a list in powershell

I have a directory full of file pairs. Each pair of files have the same name with the extensions of mp3 and cdg (karaoke files!). I would like to use powershell to get the list of all distinct file names with no extensions
I've gotten as far as:
dir -recurse -filter "*.mp3" | select-object Name | sort
But I can't quite figure out how to pass each Name to [System.IO.Path]::GetFileNameWithoutExtension
how would I do this?
What you're looking for is the for-each (%) filter (not precisely sure if it's a filter or a cmdlet but it has the same usage syntax).
Try the following
dir -recurse -filter "*.mp3" |
%{ $_.Name } |
%{ [IO::Path]::GetFileNameWithoutExtension($_) } |
sort
EDIT Update
I changed my answer from "select-object Name" to "%{ $_.Name}". The former essentially takes the Name property off of the pipeline value and creates a new object with a single property of the specified name with the value on the original object. The latter will process every value in the pipeline and pass the result of executing $_.Name down the pipeline.
dir -recurse -filter "*.mp3"| select #{name='Name';Expression={[System.IO.Path]::GetFileNameWithoutExtension($_.Name)}} | sort
If you hate typing %{$_.foo} all the time like I do, try Get-PropertyValue (alias: gpv) from PSCX.
More musings here for the suitably geeky: http://richardberg.net/blog/?p=55
Now that PowerShell v2 is RTMd, you can select the BaseName member:
dir -recurse -filter *.mp3 | select BaseName | sort