Understanding the get-culture command - powershell

I recently had some trouble with culture dependent returned values from my powershell script. The same script returned different values, depending on which machine it was.
So I thought that maybe the culture settings are different and for one server it returned.
get-culture : de-DE
for the other it was like : en-US
One value is for the keyboard settings but what does the other (second) stand for?
And is the second value bound to the OS installation or is that just a setting?
Is there a command in powershell to change the value?
Of course I first read the gelp get-help get-culture
DESCRIPTION
The Get-Culture cmdlet gets information about the current culture settings. This includes information about the
current language settings on the system, such as the keyboard layout, and the display format of items such as
numbers, currency, and dates.
But I am not satisfied with it.

The help for the Cmdlet Get-Culture contains a subheading name related links. Please note the last 2 lines.
Related Links
Online Version: http://go.microsoft.com/fwlink/p/?linkid=293965
Set-Culture
Get-UICulture
When searching for help also use the Get-Command Cmdlet.
Get-Command "*culture*"
You can view your 'current culture' by using the built in Powershell variables.
$PSCulture
$PSUICulture
The following code block returns the short date pattern of three different cultures.
### Creates an array of cultureinfo objects:
$myCulturesArray = #(
( $myDECulture = New-Object System.Globalization.CultureInfo("de-DE") ),
( $myGBCulture = New-Object System.Globalization.CultureInfo("en-GB") ),
( $myUSCulture = New-Object System.Globalization.CultureInfo("en-US") )
);
### Outputs today's date using each CultureInfo object
$myCulturesArray | foreach {
(Get-date).ToString('d', $_ )
}
Further reading:
Tobias Weltner put together a very useful set of pdfs, volume 3 is on culture.
Also, at the prompt:
Get-Help Get-Culture -Full
help about_Script_Internationalization

Related

Can someone explain the difference between Get-ComputerInfo -Property OSName vs (Get-ComputerInfo).OSName

I had some old code for documenting the particulars of my machines when I came across Get-ComputerInfo. So I tried to use it (unsuccessfully) and was almost ready to give up when I came across a way to get what I wanted. But now I'm wondering if anyone can help me understand WHY it works the way it does.
It's pretty simple to illustrate:
$OSName1 = Get-ComputerInfo -Property OSName
$OSName2 = (Get-ComputerInfo).OSName
Write-Host $OSName1
Write-Host $OSName2
The above yields:
#{OsName=Microsoft Windows 11 Pro}
Microsoft Windows 11 Pro
I was expecting the 2nd result from the 1st variable. So what's happening differently in those 2 variables? I was expecting them to be the same.
Thanks in advance!
Mark
While the comments do explain what you're already seeing, which is that Get-ComputerInfo -Property OSName returns an object with that property, what isn't obvious is why the command would do that, instead of returning what you expected: the same thing as (Get-ComputerInfo).OSName.
The reason becomes more clear when looking at the help:
Get-Help Get-ComputerInfo
NAME
Get-ComputerInfo
SYNTAX
Get-ComputerInfo [[-Property] <string[]>] [<CommonParameters>]
ALIASES
gin
In the syntax, we can see that -Property takes a String array and not just a single string.
So you can pass an array of property names to return, like this for example:
$info = Get-ComputerInfo -Property OSName, TimeZone, OSProductType
Write-Host $info
And then get:
#{TimeZone=(UTC-05:00) Eastern Time (US & Canada); OsName=Microsoft Windows 10 Enterprise; OsProductType=WorkStation}
Now, I did it that way to match your question, but if you just output the value naturally, or via Write-Output, it would look a lot nicer:
$info
Write-Output $info
TimeZone OsName OsProductType
-------- ------ -------------
(UTC-05:00) Eastern Time (US & Canada) Microsoft Windows 10 Enterprise WorkStation
Or you could format it:
$info | Format-List
TimeZone : (UTC-05:00) Eastern Time (US & Canada)
OsName : Microsoft Windows 10 Enterprise
OsProductType : WorkStation
So the answer why it returns an object with a single property, it's for consistency for when you request multiple properties (or request none and get them all).
This property selection works exactly the same as if you had used Select-Object:
$info = Get-ComputerInfo | Select-Object -Property TimeZone, OSName, OSProductType
It's also worth noting that Get-ComputerInfo is quite slow, and that selecting individual properties does not speed it up in any way, so there is no particular advantage to using the built-in selection vs. selecting after the fact, other than conciseness and readability.
If you need multiple properties, it's definitely not a good idea to make multiple calls to Get-ComputerInfo with different properties selected, as you're retrieving all the same info on every call and then discarding most of it that way.
If you're unsure which properties you need in the first call, just get them all, and choose them later:
$info = Get-ComputerInfo
if ($condition1) {
# do thing with $info.OSName
}
if ($condition2) {
# do thing with $info.TimeZone
}
# etc.

Running help <command> and piping output to Where-Object or Select-Object returns empty rows

Running the command help firewall | Select-Object Category. The result is one column blank Category.
The strange thing is that the empty rows number represent the amount of rows that help firewall would result without calling piping it to Select-Object
Or I'm trying to filter the output of help firewall to return only rows with Name that starts with "Get". Running help firewall | Where-Object Name -like "Get" just returns nothing.
Why aren't these pipes on help working? They are working perfectly on other commands.
Powershell Version 5.1 and using default windows console.
To complement Zilog80's helpful answer with background information:
Get-Command help reveals that help is not a mere alias of the Get-Help cmdlet, but a (built-in) function (submit $function:help to see its definition).
As you've noticed yourself:
while Get-Help outputs an object ([pscsustomobject]) with properties that reflect help-topic metadata such as Category, which is then rendered as display text by PowerShell's output-formatting system,
the help function returns strings - a stream of text lines representing the rendered help topic - of necessity.
You can observe the difference in output type by piping to the Get-Member cmdlet (help firewall | Get-Member vs. Get-Help firewall | Get-Member)
The purpose of the help function is to wrap Get-Help with interactive paging, to allow convenient navigation through lengthy help topics that don't fit onto a single console (terminal) screen.
This paging is provided via an external program (by default, more.com on Windows, and less on Unix-like platforms, configurable via $env:PAGER, but only in PowerShell (Core) 7+), and since PowerShell only "speaks text" when communicating with external programs, help must send a stream of strings (lines for display) to them, which it does via
Out-String -Stream.
Note:
When the external paging programs find that their stdout stream isn't connected to a console (terminal), they take no action other than simply passing the input through (in Unix terms, they then behave like cat).
Hypothetically, the help function itself could determine this condition and then (a) not pipe to the paging program and (b) relay the object output by Get-Help as-is.[1] However, determining a command's output target from inside that command, using PowerShell code, may not even be possible.
[1] The function actually already takes this action when a custom pager defined via $env:PAGER is found to be a PowerShell command rather than an external program.
Check the feedback from help help in PowerShell :
You can also type `help` or `man`, which displays one screen of text at a
time. Or, ` -?`, that is identical to `Get-Help`, but only
works for cmdlets.
The helpcommand display "screen of text" which means it is outputting [System.String] objects, not [PSCustomObject] objects.
Only -? will behave identically to Get-help and will provide [PSCustomObject] objects.
To see what's going on, check the different output from :
help firewall | %{ $_.GetType() }
And
Get-help firewall | %{ $_.GetType() }
And, for cmdlet,
Select-Object -? | %{ $_.gettype() }

Temporarily change powershell language to English?

I wrote some software that uses the output of system (powershell) commands, but did not foresee that the output would be different for languages other than English.
Is there a way to temporarily change the language in Powershell to English for just that one, single powershell session?
Notes
In case it is of significance, the particular powershell code I wish to run is netstat -n -a
I have come across some ways to change powershell language (e.g. here, here). But I want to be careful not to change it permanently! (that would be bad)
(a) For external programs such as netstat.exe, there is unfortunately no way (that I know of) to change the UI language in-session:
On Windows Server 2012 / Windows 8 and above, the Set-WinUILanguageOverride cmdlet allows you to (persistently) change the system-wide UI language for the current user, but that only takes effect in future logon sessions - that is, logging off and back on or a reboot are required.
As an aside: On Windows Server 2012 / Windows 8 and above, there is also the Set-Culture cmdlet, but its purpose is not to change the UI culture (display language), but only culture-specific settings such as date, number, and currency formats. It too changes the setting persistently for the current user, but only requires a new session (process) for the change to take effect.
(b) For PowerShell commands and .NET types, there is an in-session (non-persistent) solution - assuming the commands are culture-aware and come with localized strings:
Set [cultureinfo]::CurrentUICulture (temporarily) to the desired culture name (use [cultureinfo]::GetCultures('SpecificCultures') to see all predefined ones) ; e.g., [cultureinfo]::CurrentUICulture = 'en-US'
Complementarily, you may want to set [cultureinfo]::CurrentCulture (note the missing UI part) as well, which determines the culture-specific number, date, ... formatting.
In older versions of PowerShell / .NET, you'll have to set these properties on [System.Threading.Thread]::CurrentThread instead; e.g.,
[System.Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'
See the bottom section for helper function Use-Culture that wraps this functionality for execution of code with a different culture temporarily in effect; here's an example call
with the culture-sensitive Get-LocalGroupMember cmdlet:
# Try with values other than "en-US", e.g. "fr-FR" to see localized
# values in the "ObjectClass" output column.
Use-Culture en-US { Get-LocalGroupMember Administrators }
An ad hoc example, if you don't want to define a helper function (only the UI culture is changed here):
& {
$prev=[cultureinfo]::CurrentUICulture
[cultureinfo]::CurrentUICulture='en-US'
Get-LocalGroupMember Administrators
[cultureinfo]::CurrentUICulture=$prev
}
Caveats:
PowerShell [Core] itself is not localized yet, as of v7.2.x; progress is being tracked in GitHub issue #666; however, the solution below does work with third-party modules that ship with localized messages and help content, as well as select Windows-specific modules that talk to platform APIs, such as the Microsoft.PowerShell.LocalAccounts module, whose Get-LocalGroupMember cmdlet was used in the example above.
Due to a bug in Windows PowerShell (PowerShell [Core] v6+ is not affected), in-session changes to [cultureinfo]::CurrentUICulture and [cultureinfo]::CurrentCulture are automatically reset at the command prompt, whenever a command finishes executing; however, for a given script the changes remain in effect for the entire script and its callees - see this answer.
Taking a step back:
I wrote some software that uses the output of system (powershell) commands, but did not foresee that the output would be different for languages other than English.
This is precisely why it's generally worth looking for PowerShell-native solutions as opposed to calling external programs:
Instead of having to parse - possibly localized - text, as with netstat.exe, for instance, PowerShell commands return objects whose properties you can robustly access in a culture-independent fashion.
Specifically, Mathias R. Jessen suggests looking at Get-NetTCPConnection as a PowerShell alternative to netstat.exe (available on Windows Server 2012 / Windows 8 and above).
Function Use-Culture's source code:
Note: The code was gratefully adapted from this venerable blog post; it is designed
# Runs a script block in the context of the specified culture, without changing
# the session's culture persistently.
# Handy for quickly testing the behavior of a command in the context of a different culture.
# Example:
# Use-Culture fr-FR { Get-Date }
function Use-Culture
{
param(
[Parameter(Mandatory)] [cultureinfo] $Culture,
[Parameter(Mandatory)] [scriptblock] $ScriptBlock
)
# Note: In Windows 10, a culture-info object can be created from *any* string.
# However, an identifier that does't refer to a *predefined* culture is
# reflected in .LCID containing 4096 (0x1000)
if ($Culture.LCID -eq 4096) { Throw "Unrecognized culture: $($Culture.DisplayName)" }
# Save the current culture / UI culture values.
$PrevCultures = [Threading.Thread]::CurrentThread.CurrentCulture, [Threading.Thread]::CurrentThread.CurrentUICulture
try {
# (Temporarily) set the culture and UI culture for the current thread.
[Threading.Thread]::CurrentThread.CurrentCulture = [Threading.Thread]::CurrentThread.CurrentUICulture = $Culture
# Now invoke the given code.
& $ScriptBlock
}
finally {
# Restore the previous culture / UI culture values.
[Threading.Thread]::CurrentThread.CurrentCulture = $PrevCultures[0]
[Threading.Thread]::CurrentThread.CurrentUICulture = $PrevCultures[1]
}
}
Original author of this code is #Scepticalist.
Run this from powershell console. It will change the culture to en-US for current session.
function Set-CultureWin([System.Globalization.CultureInfo] $culture) { [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture ; [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture } ; Set-CultureWin en-US ; [system.threading.thread]::currentthread.currentculture
Then you have to use the command Get-NetTCPConnection Instead of netstat. For its usage see https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection?view=win10-ps

may i know the difference between [cultureinfo]::InstalledUICulture and get-uiculture in powershell?

may i know the difference between [cultureinfo]::InstalledUICulture and get-uiculture in powershell?
i tried [cultureinfo]::InstalledUICulture in windows 10 and works well but not in windows 7. so i got command get-uiculture in powershell but not sure if it gives same result as [cultureinfo]::InstalledUICulture.
Cultures are a little weird honestly, You can View the source code for Get-UICulture here
the command simply returns the value of $host.CurrentUICulture which can be different from $host.CurrentCulture
on my system currently I get the following:
Write-Host "Current Culture: $((Get-Culture).Name)"
Write-Host "Current UI Culture: $((Get-UICulture).Name)"
Current Culture: en-IE
Current UI Culture: en-US
for [CultureInfo]::InstalledUICulture you can see the difference between it and [CultureInfo]::UICulture : here
CurrentCulture
Gets or sets the CultureInfo object that represents the culture used by the current thread.
CurrentUICulture
Gets or sets the CultureInfo object that represents the current user interface culture used by the Resource Manager to look up culture-specific resources at run time.
InstalledUICulture
Gets the CultureInfo that represents the culture installed with the operating system.
hopefully that clears it up a little for you.
I'm running WMF 5.1 on Windows 7, but the only difference that I see is that [System.Globalization.CultureInfo]::InstalledUICulture has IsReadOnly set to $true, while Get-UICulture has IsReadOnly set to $false.
That means it's like the difference between these:
[System.Globalization.CultureInfo]::New('en-US')
[System.Globalization.CultureInfo]::ReadOnly([System.Globalization.CultureInfo]::New('en-US'))
Or:
[System.Globalization.CultureInfo]::New([System.Globalization.CultureInfo]::InstalledUICulture)
[System.Globalization.CultureInfo]::ReadOnly([System.Globalization.CultureInfo]::InstalledUICulture)
Looking internally, however, it looks like Get-UICulture is a rather pointless command. All it really does is return (Get-Host).CurrentUICulture or $host.CurrentUICulture, both of which have IsReadOnly set to $false.

Forcing PowerShell errors output in English on localized systems

I need to run some PowerShell scripts across various operating systems. Most of them are in English version, however, some are localized for example German, French, Spanish, etc. The problem is local system administrators mostly do not now PowerShell and in the case the script fails and throws an error at them, instead of reading it they just send screenshots of such error messages to me and if the cause to this error is not obvious I am stuck with typing it to g. translate to find out what is going on.
Is there a switch I can run the whole script or single command with or a parameter or any other way to force errors in PowerShell to be displayed in English instead of the language that is default for that particular machine?
You can change the pipeline thread's CurrrentUICulture like so:
[Threading.Thread]::CurrentThread.CurrentUICulture = 'fr-FR'; Get-Help Get-Process
I'm on an English system but before I executed the line above, I updated help like so:
Update-Help -UICulture fr-FR
With that, the Get-Help call above gave me French help on my English system. Note: if I put the call to Get-Help on a new line, it doesn't work. Confirmed that PowerShell resets the CurrentUICulture before the start of each pipeline which is why it works when the commands are in the same pipeline.
In your case, you would need to have folks install English help using:
Update-Help -UICulture en-US
And then execute your script like so:
[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'; .\myscript.ps1
[Threading.Thread]::CurrentThread.CurrentUICulture only affects to current one-liner, so you can use it for execution of single .ps1 file.
If you want to change messages to English throughout every command in a PowerShell window, you have to change the culture setting cached in PowerShell runtime with reflection like this:
# example: Set-PowerShellUICulture -Name "en-US"
function Set-PowerShellUICulture {
param([Parameter(Mandatory=$true)]
[string]$Name)
process {
$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture($Name)
$assembly = [System.Reflection.Assembly]::Load("System.Management.Automation")
$type = $assembly.GetType("Microsoft.PowerShell.NativeCultureResolver")
$field = $type.GetField("m_uiCulture", [Reflection.BindingFlags]::NonPublic -bor [Reflection.BindingFlags]::Static)
$field.SetValue($null, $culture)
}
}
(from https://gist.github.com/sunnyone/7486486)