grep gci output in powershell - 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

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).

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

Powershell -- Get-ChildItem Directory full path and lastaccesstime

I am attempting to output full directory path and lastaccesstime in one line.
Needed --
R:\Directory1\Directory2\Directory3, March 10, 1015
What I am getting --
R:\Directory1\Directory2\Directory3
March 10, 1015
Here is my code, It isn't that complicated, but it is beyond me.
Get-ChildItem -Path "R:\" -Directory | foreach-object -process{$_.FullName, $_.LastAccessTime} | Where{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) } | Out-File c:\temp\test.csv
I have used foreach-object in the past in order to ensure I do not truncate the excessively long directory names and paths, but never used it when pulling two properties. I would like the information to be on all one line, but haven't been successful. Thanks in advance for the assist.
I recommend filtering (Where-Object) before selecting the properties you want. Also I think you want to replace ForEach-Object with Select-Object, and lastly I think you want Export-Csv rather than Out-File. Example:
Get-ChildItem -Path "R:\" -Directory |
Where-Object { $_.LastAccessTime -lt [DateTime]::Today.AddYears(-2) } |
Select-Object FullName,LastAccessTime |
Export-Csv C:\temp\test.csv -NoTypeInformation
We can get your output on one line pretty easily, but to make it easy to read we may have to split your script out to multiple lines. I'd recommend saving the script below as a ".ps1" which would allow you to right click and select "run with powershell" to make it easier in the future. This script could be modified to play around with more inputs and variables in order to make it more modular and work in more situations, but for now we'll work with the constants you provided.
$dirs = Get-ChildItem -Path "R:\" -Directory
We'll keep the first line you made, since that is solid and there's nothing to change.
$arr = $dirs | Select-Object {$_.FullName, $_.LastAccessTime} | Where-Object{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) }
For the second line, we'll use "Select-Object" instead. In my opinion, it's a lot easier to create an array this way. We'll want to deal with the answers as an array since it'll be easiest to post the key,value pairs next to each other this way. I've expanded your "Where" to "Where-Object" since it's best practice to use the full cmdlet name instead of the alias.
Lastly, we'll want to convert our "$arr" object to csv before putting in the temp out-file.
ConvertTo-CSV $arr | Out-File "C:\Temp\test.csv"
Putting it all together, your final script will look like this:
$dirs = Get-ChildItem -Path "C:\git" -Directory
$arr = $dirs | Select-Object {$_.FullName, $_.LastAccessTime} | Where{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) }
ConvertTo-CSV $arr | Out-File "C:\Temp\test.csv"
Again, you can take this further by creating a function, binding it to a cmdlet, and creating parameters for your path, output file, and all that fun stuff.
Let me know if this helps!

PowerShell Out-file manipulation

i hope someone can help.
I am trying to manipulate a file created by powershell.
I managed to get to the end result that i want, but i am sure it would be easier if it was only one command.
# Invoke the Exchange Snapping ( make sure you are Exchange Admin to do it SO)
add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
#Create a file with list of DL in the organization
Get-DistributionGroup | Select-Object Name | Out-File C:\Pre_DLGroups.txt
$content = Get-Content C:\Pre_DLGroups.txt
#Remove the 3 first lines of the file that you dont need it
$content | Select-Object -Skip 3 | Out-file C:\DLGroups.txt
#Trim the space in the end and crate the Final file
Get-Content C:\DLGroups.txt | Foreach {$_.TrimEnd()} | Set-Content c:\FinalDLGroup.txt
is that way to make the end result in a single file rather than creating 3?
cheers
Elton
You can send your content across the pipeline without writing it out to files. You can use parenthesis to group the output of certain sets of cmdlets and/or functions, and then pipe that output through to the intended cmdlets.
This can all be applied on a single line, but I've written it here on multiple lines for formatting reasons. The addition of Out-String is something of a safety measure to ensure that whatever output you're intending to trim can actually be trimmed.
Since we're not getting this content from a text file anymore, powershell could possibly return an object that doesn't understand TrimEnd(), so we need to be ready for that.
(Get-DistributionGroup | Select-Object Name) |
Out-String |
Select-Object -Skip 3 |
Foreach {$_.TrimEnd()} |
Set-Content c:\FinalDLGroup.txt
However, an even smaller solution would involve just pulling each name and manipulating it directly. I'm using % here as an alias for Foreach-Object. This example uses Get-ChildItem, where I have some files named test in my current directory:
(Get-ChildItem test*) |
% { $_.Name.TrimEnd() } |
Set-Content c:\output.txt
Get-DistributionGroup |
Select-Object -ExpandProperty Name -Skip 3 |
Set-Content c:\FinalDLGroup.txt

Filter the output of a command as if it was text

I have a simple question, but I am also a beginner in PowerShell. I think it has to do with the fact that the output of the Get-Process command (alias ps) is objects and not text.
I want to get a list of the services running that have the name "sql" in them.
This is what I tried so far, but every attempt returns nothing:
Get-Service | where {$_ -match 'sql'}
Get-Service | where {$_ -like 'sql'}
Get-Service | Select-String sql
I am looking for a pattern that lets me treat the output of every command as searchable text.
Just forget it :o)
Outputs are objects. You are right, and you are going to use this.
So mjolinor has the shortest answer, but for your knowledge just test:
Get-Service | Get-Member
So you will understand that
Get-Service | Where-Object {$_.name -match ".*sql.*" }
also works, and there you've got your text as a property of the object.
Most answers here focus on finding the service name with "sql" in the name, not on filtering the entire output as if it was text. Also, the accepted answer uses a non-PowerShell function, "findstr".
So, granted, what follows is not the most elegant solution, but for sake of completeness I would like to provide the 100% PowerShell solution that takes the question of the OP literally:
(get-Service | Out-String) -split "`r`n" | Select-String sql
We need Out-String, because using the solutions provided in other answers doesn't provide us the full text output of the Get-Service command, only the Name parameter.
We need to split on newlines, because Select-String seems to treat the entire text as one long string, and returns it as a whole, if "sql" is found in it.
I use Select-String instead of findstr, because findstr is not a PowerShell function.
This is a purist answer, and in practice, for this specific use-case, I would not recommend it. But for people coming here through Google Search based on the question title, this is a more accurate answer...
Get-Service | Select-String -Pattern "sql"
This works just like grep. And you can even sort:
Get-Service | Select-String -Pattern "sql" | sort
The other answers are right of course about your specific question of starting services that have "sql" in their name, but to answer the generic question:
You can do Get-Service | Out-String, and you will get the output as string, much like how Unix commands work.
Also when the output is piped to non-PowerShell commands, it does get converted to text, so for example: Get-Service | grep sql would work the way you wanted.
But again, like #JPBlanc says, it is good embrace the way PowerShell works, which is that the outputs are objects. It gives you way more control and keeps things simple and readable (the Unix commands with sed, awk and what not operating on text output of other command outputs can get very cryptic!).
You're working way too hard at it:
Get-Service *sql*
If anyone wants more information on logical operations, please see Using the Where-Object Cmdlet:
• -lt -- Less than
• -le -- Less than or equal to
• -gt -- Greater than
• -ge -- Greater than or equal to
• -eq -- Equal to
• -ne -- Not equal to
• -like - Like; uses wildcards for pattern matching
Get-Service | where {$_ -match 'sql'} would be Get-Service | where {$_ -eq "sql"}
Get-Service | where {$_ -like 'sql'} would be Get-Service | where {$_ -like "sql"}
And now an actual example.
PS C:\> Get-Service | where {$_.name -like "net*"}
Status Name DisplayName
------ ---- -----------
Running Net Driver HPZ12 Net Driver HPZ12
Running Netlogon Netlogon
That the text of the name is a property of the object is important to get your head around, and how to use the property values in a filter.
Another aspect of PowerShell you can leverage to solve this is selecting properties out of objects with Select-Object (alias select):
Get-Service | select -expand name
will get you a string array with the names of the servers, and two of your original three filters would work on that. The -like isn't going to work, because there's no wildcards in the test string. The only thing it will ever match is just 'sql'.
I still believe the first solution I posted is best. It's important to know how to do late filtering, but also how to use early filtering when you can.
If you want to list all services with "sql" in the service name, just use:
get-service -name *sql*
You probably want this:
Function Select-ObjectPropertyValues {
param(
[Parameter(Mandatory=$true, Position=0)]
[String]
$Pattern,
[Parameter(ValueFromPipeline)]
$input
)
$input | Where-Object {($_.PSObject.Properties | Where-Object {$_.Value -match $Pattern} | Measure-Object).count -gt 0} | Write-Output
}
Here we are going though each property of an object to see if it matches the given pattern. If the object contains one or more such properties, we write it out. End result: grep by all properties of an object.
Put it in your configuration files and grep to your heart's content.
how about:
Get-Service| Out-String -stream | Select-String sql
where the key point is that -stream option converts the Out-String output in separate lines of text.