Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell script to take backup of all environment variables & save it in a file

I am new to PowerShell and recently started learning about some of its commands.

Now I am looking to try adding, editing and deleting any path entry from the environment Path variable (System).

But I am concerned about the case if anything goes wrong, so I want to take backup of all environment variables in case I need to restore.

I have the following cmdlet to display specific variable onto the screen, but can anyone let me know how to take backup of all environment variables (both system and user) to a text file through a .ps1 PowerShell script?

Get-ChildItem Env:Path
like image 556
Vicky Dev Avatar asked Nov 24 '17 21:11

Vicky Dev


1 Answers

Note: PowerShell's env: drive only reflects the environment variables of the current process - to persistently change the system's or the current user's environment variables, you must use the .NET framework directly - see this answer of mine.

The following snippet, which uses env:, therefore only shows how to save and restore all environment variables of the current process:

# Save all the process' environment variables in CLIXML format.
Get-ChildItem env: | Export-CliXml ./env-vars.clixml

# ... modify the env. variables

# Restore the previously saved env. variables.
Import-CliXml ./env-vars.clixml | % { Set-Item "env:$($_.Name)" $_.Value }

Note that this will not remove any new env. variables you may have created in the meantime, however - extra work would be needed to eliminate those.


Note: Export-CliXml and Import-CliXml are being used, because they enable robust round-tripping (serialization / deserialization) of values (although you cannot generally recreate objects of the very same type, only deserialized look-alikes).

By contrast, using something like Get-ChildItem env: > ./env-vars.txt or its equivalent, Get-ChildItem env: | Out-File ./env-vars.txt simply saves the Get-ChildItem output as plain text the way it displays in the console, which is not a format suitable for machine parsing (deserialization).


Optional reading: why Import-CliXml ./env-vars.clixml | Set-Item should work, but doesn't:

Set-Item is designed to bind parameter values via the pipeline, notably -LiteralPath (alias -PSPath) and -Value.

Therefore, if Set-Item receives objects through the pipeline that have properties by those names (.PSPath, .Value), they should automatically bind to the -PSPath (-LiteralPath) and -Value parameters, enabling invocation such as ... | Set-Item - so there should be no need for explicit enumeration and explicit parameter-passing via % (ForEach-Object), as used above.

Unfortunately, however, as of Windows PowerShell v5.1 / PowerShell Core v6.0.1, the -Value parameter is defined in a way that also binds the input object as a whole to -Value (ValueFromPipeline), which - given that -Value is defined generically as type [System.Object] - invariably takes precedence over looking for the object's .Value property (ValueFromPipelineByPropertyName) - in other words: no input object, irrespective of its type, is ever bound by its .Value property - any input object is bound as itself.

In the case at hand, the objects output by Import-CliXml are of type [Deserialized.System.Collections.DictionaryEntry]; they are bound to -Value, and then converted to a string when the environment variable is set (environment variables can only ever contain string values); the string representation of that type is System.Collections.DictionaryEntry - irrespective of its .Value property - and that generic string is therefore - uselessly - set as the environment variable's value.

This problematic behavior has been reported on GitHub.

like image 90
mklement0 Avatar answered Sep 22 '22 17:09

mklement0