1

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.

deostroll
  • 11,661
  • 21
  • 90
  • 161

3 Answers3

4

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.

Bacon Bits
  • 30,782
  • 5
  • 59
  • 66
2

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"

YorSubs
  • 3,194
  • 7
  • 37
  • 60
1

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