What is the difference between Env: and [System.Environment]? - powershell

Given #mklement's good answer in how to sort a txt file in specific order in Powershell, it made me wonder...
What is the difference between Env: and [System.Environment]?
Why is [Environment]::NewLine available, but $Env:NewLine does not exist?

[System.Environment] is the .Net framework's static environment class. It offers static methods related to the "environment" including ways to get environment variables.
$env:WHATEVER is special variable syntax whereby you can access the contents of a PSProvider using variable semantics.
What is a PSProvider? (also see about_Providers)
It's basically a way to access hierarchical data stores through a singular interface that is similar to a filesystem.
In fact FileSystem is itself a PSProvider in PowerShell, and this is why cmdlets that deal with files don't mention files (i.e.: Get-ChildItem, Get-Content, Set-Location, etc.).
To see available providers, use Get-PSProvider:
Name Capabilities Drives
---- ------------ ------
Registry ShouldProcess, Transactions {HKLM, HKCU}
Alias ShouldProcess {Alias}
Environment ShouldProcess {Env}
FileSystem Filter, ShouldProcess, Credentials {A, C, D, P...}
Function ShouldProcess {Function}
Variable ShouldProcess {Variable}
Certificate ShouldProcess {Cert}
WSMan Credentials {WSMan}
Use Get-PSDrive to just see the drives themselves:
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
A 103.23 46.58 FileSystem A:\
Alias Alias
C 200.02 22.77 FileSystem C:\
Cert Certificate \
D 1048.88 2677.13 FileSystem D:\
Env Environment
Function Function
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
O 49.34 10.16 FileSystem O:\
P 335.32 176.50 FileSystem P:\
S FileSystem S:\
Variable Variable
WSMan WSMan
Environment is also a PSProvider, which you can see by trying to navigate to its PSDrive:
Set-Location Env:
Or browsing it:
Get-ChildItem Env:
Or even getting its contents:
Get-Content Env:\COMPUTERNAME
Get-ChildItem Env: | Get-Content
The special variable syntax is a shorthand way of accessing certain PSProviders (they don't all support it), and it's most often used with Environment (I'd venture a guess that syntax was created specifically for the Environment).
That syntax does actually work for the file system but it's pretty awkward
${C:\users\briantist\test.txt}
Tab completion doesn't work correctly with that syntax.
If you try it for something like the registry provider, it will tab complete but throw an exception about it not being implemented when you run it.
Here's a fun useless one: use the Variable:\ provider:
$test = 'test'
$Variable:test

Related

How to refer to HKEY_CLASSES_ROOT in PowerShell?

New-Item -Path "HKCR:\Directory\Background\shell\customname" -Force
I've been doing the same thing for HKCU and KHLM but when I try HKCR I get errors in PowerShell. how am I supposed to do it for HKEY_CLASSES_ROOT?
I searched for a solution but couldn't find any.
Okay I figured out on my own,
checked Get-PSDrive
and saw the only registry aliases available by default on Windows/PowerShell are
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
so, what I did, following this, was to add a new alias for HKEY_CLASSES_ROOT that is called HKCR
New-PSDrive -Name "HKCR" -PSProvider Registry -Root "HKEY_CLASSES_ROOT"
Defining a custom drive whose root is HKEY_CLASSES_ROOT, as shown in your own answer, is definitely an option, especially for repeated use.
Ad hoc, you can alternatively use the Registry:: provider prefix directly with native registry paths:
New-Item -Path 'Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\customname' -Force
Note:
The Registry part of the prefix is the provider name, as shown in Get-PSProvider's output.
Hypothetically, multiple providers with the same name could be registered, in which case you can prefix the name with the implementing module name for disambiguation; in the case of the registry provider, this module-qualified prefix is Microsoft.PowerShell.Core\Registry::[1] However, it's fair to assume that no third-party providers will choose a name that conflicts with the providers that ship with PowerShell, so Registry:: (or registry::, case doesn't matter), should do.
Note that the module-qualified provider name does show up in the prefix of the .PSPath property that provider items, such as reported by Get-Item and Get-ChildItem, are decorated with, e.g.:
PS> (Get-Item HKCU:\Console).PSPath
Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Console
[1] Note that the Core part of the name does not refer to the PowerShell (Core) edition; it simply denotes a module that is at the core of either edition.

Alternative to test-path, using credential, to hosts on a different domain, with wildcard in folder path

I'm trying to copy a file to a specific folder on one of n hosts (hostA, hostB etc.), but i don't know the full path of the folder.
If I don't use a credential (which I have to do) I can e.g.
test-path -path \\hostA\d$\*\targetFolder ...and hit D:\blah\targetFolder
I could use the credential with new-psdrive, but then I can't map to a wildcarded path. I could also invoke-command, but then I'd have to work out a way to get the file from the sourceHost...
This is for a TFS/AzureDevops pipe.
Using New-PSDrive, map a (non-persistent, PS-only) drive to the admin share \\hostA\d$ itself, and then use that drive for wildcard-based path testing:
# Define a PS-only RemoteD: drive that maps to \\hostA\d$,
# using the specified credentials.
New-PSDrive RemoteD FileSystem \\hostA\d$ -Credential (Get-Credential)
# Use paths based on RemoteD: for wildcard-based testing.
Test-Path RemoteD:\*\targetFolder

Is it possible to create namespaced environment variables in powershell? (like $custom:JSenv)

I was wondering if I can create a PSDrive like Env: for my project specific environment variables, like $custom:JSenv, $custom:root etc. instead of populating the Env: PSDrive.
I tried using New-PSDrive -Name custom -PSProvider Environment. I expexceted to see the custom: drive. However, in the actual output nothing changed.
Is creating aEnvironment PSDrive the right way to achieve the purpose?
The New-PSDrive cmdlet requires a Root parameter. I was unable to replicate your problem:
PS /> $null = New-PSDrive -Name custom -PSProvider Environment -Root ''
PS /> Get-Item -Path custom:
# => environment variables
All this really does, however, is give you a different method of calling the Env: drive since they use the same provider and the provider does not have a concept of scope.
No: To get what you want, you'd have to implement your own PowerShell [drive] provider that also implements the IContentCmdletProvider interface, because $<drive>:<path> (or ${<drive>:<path>}) is syntactic sugar - called namespace notation - for the following command:
Get-Content -Path <drive>:<path>
See the bottom section for more.
As for what you tried:
While it is possible to define a custom drive based on an existing provider, that drive will invariably reflect that provider's items, without any ability to define your own.
In other words:
New-PSDrive -Name custom -PSProvider Environment -Root ''
will simply make drive custom: an alias of the env: drive.
While you could define your custom entries as environment variables, they would show in addition to the preexisting ones.
Implementing a custom PowerShell drive [provider]:
Writing your own [drive] provider requires compiled code, so you cannot implement custom drives in PowerShell itself.
As of Windows PowerShell v5.1 / PowerShell Core 6.1.0, implementing providers is nontrivial, but simplifying that is being considered for a future PowerShell Core version.
However, there are third-party helper modules that greatly simplify the process and do allow you to implement custom drives in PowerShell code.
Note: I haven't verified that your specific use case can be implemented.
Simplex: PS Gallery link - source code and documentation - easy-to-use DSL, but not all features are exposed.
SHiPS: PS Gallery link - source code and documentation - more complex, but more fully-featured.

Getting disk usage using powershell command

I am trying to get the disk usage using the below command.
C:\Users\arjun> (Get-PSDrive $drivename)
WARNING: column "CurrentLocation" does not fit into the display and was removed
.
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
A FileSystem A:\
Alias Alias
C 45.29 4.70 FileSystem C:\
cert Certificate \
D 36.86 13.14 FileSystem D:\
E 230.36 19.64 FileSystem E:\
Env Environment
Function Function
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
T 10.84 39.16 FileSystem T:\
Variable Variable
WSMan WSMan
Y FileSystem Y:\
I am getting the above result, however when I try to fetch one column its failing.
(Get-PSDrive $drivename).Used..
Note that is is legacy windows system. Is there any alternative for this. I tried the same command on non-legacy systems and its works fine.
Hello I see what you are doing there might seem rather logical but powershell doesn't know which drive you are defining. So what you should be doing is the following.
$drivename.PSDrive.Used
This way it will exactly know which drive is meant. As Output you will get Byte so you should propably save it into an Variable and then format it.
What you should also be aware of is saving the variable $drivename correctly. That mean that you should save it alike this.
$drivename = Get-Item "C:\Users\arjun"
So it could be that you perhaps forgot to save the object instead of only the path name, or maybe you forgot the quotemarks. Please be sure to check this and mark this answered if it helped you out.

Powershell - Why does $ENV | GET-MEMBER not work?

I come from a C, C++, C#, python background so i'm applying this thought pattern to Powershell which i'm learning from scratch but I'm a little confused so far as at first glance it seems to be inconsistent and does not follow a fixed base class type structure for all objects so that things can be queried in a consistent manner.
The following works fine:
$host | get-member
$env:username
So $env is a valid object but this does not work:
$env | get-member
These also do not work:
$env.gettype()
dir $env
dir $env:
but this type query on $host does so I'm assuming $host is a .net variable but $env is not?
$host.gettype()
I found that env: also works with dir (aka get-childitem) but this colon is yet another type of notation i'm unfamiliar with and things are starting to get very confusing now. This does not seem to be a string format in this case which I have seen some google posts about so what is it? It behaves like a member selection or dictionary key specifier. If it is a member selector or dictionary key then i would expect get-member to work because it is a standard object.
This outputs the variables and values that I wanted but I don't understand why this syntax is used. This is not DOS syntax either so what's going on here?
dir env:
But dir $env seems to equate to dir $env:userprofile???? why?
Therefore $host appears to be a .net object but $env or env: is something else completely different and I've no idea what object type it is in the grand scheme of things and cannot seem to query it's type with by conventional means. Initial thoughts are that it is a list object of sorts because get-childitem works with it but other than that I'm completely lost.
I'm clearly missing something here so can someone steer me in the right direction please?
Get-Help 'about_Providers' -ShowWindow shows that env: is drive in Environment Provider, i.e. one of Windows PowerShell providers.
BUILT-IN PROVIDERS: Windows PowerShell includes a set of built-in
providers that you can use to access the different types of data
stores.
Provider Drive Data store
-------- ----- ----------
Alias Alias: Windows PowerShell aliases
Certificate Cert: x509 certificates for digital signatures
Environment Env: Windows environment variables
FileSystem * File system drives, directories, and files
Function Function: Windows PowerShell functions
Registry HKLM:, HKCU: Windows registry
Variable Variable: Windows PowerShell variables
WSMan WSMan: WS-Management configuration information
* The FileSystem drives vary on each system.
You can also create your own Windows PowerShell providers, and you can
install providers that others develop. To list the providers that are
available in your session, type: get-psprovider.
That's why Get-ChildItem env: works in contrary to dir $env:, dir $env etc.
First thing to note is that $env and $env:username are not related. $env is just a variable and normally it does not exists, because nobody assign anything to it. Using colon in variable name (like $env:username, with exception to some predefined prefixes: global:, script:, local:, private: and variable:) is a special syntax, which allows to access to PowerShell provider item content with variable syntax. It works with any PowerShell provider which implement content cmdlets: ${C:\Windows\System.ini} or $function:prompt. That syntax is equivalent of calling of Get-Content or Set-Content for given PowerShell path.
My 2 cents:
Try get-psdrive and you will get something like:
Name Used (GB) Free (GB) Provider Root
Env Environment
So it seems to be something like a driver in batch.