How to store text variable to file and retrieve it in PowerShell?

57 Views Asked by At

Following line reads registry value into variable

$renames = (Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\Session Manager").PendingFileRenameOperations

If I use the variable directly to create new registry entry, it works well, new value is identical to previous one.

Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Session Manager" -name PendingFileRenameOperationsNew -value $renames -type MultiString

I need to store the variable to file and use it from other script. I tried to use this way

$renames | Out-File C:\temp\renames.txt
$renamesFromFile = Get-Content C:\temp\renames.txt -Raw
Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Session Manager" -name PendingFileRenameOperationsThroughFile -value $renamesFromFile -type MultiString

Unfortunately this leads into few extra bytes at the end of each line of the registry value. How to store and restore the registry value correctly?

(I need the code in PowerShell version 5.1 (default in Windows today))

2

There are 2 best solutions below

0
Mathias R. Jessen On BEST ANSWER

Dereferencing a multi-string registry value with more than 1 value set will, perhaps unsurprisingly, give you multiple string values - so $renames likely contains an array of strings after the first operation.

Out-File then writes each of these values to a separate line in the target file.

Reading it back from disk with Get-Content -Raw causes PowerShell to treat the entire file as one big multi-line string....

Unfortunately this leads into few extra bytes at the end of each line of the registry value.

Those extra bytes are likely literal carriage returns (ASCII 0xA) as read from the (raw) underlying filestream.

If you remove the -Raw switch parameter, Get-Content will instead read 1 line at a time, stripped of any trailing newlines - just like the original values in $renames:

PS ~> $renamesFromFile = Get-Content C:\temp\renames.txt -Raw
PS ~> $renamesFromFile.GetType()   # it's a single [string] value, not an array
PS ~> $renamesFromFile = Get-Content C:\temp\renames.txt
PS ~> $renamesFromFile.GetType()   # now it's an array type (eg. [object[]])
0
Darin On

PowerShell has a built-in method for transmitting data by first creating an XML-based representation of an object or objects, which is called a CliXml. This Xml form of the data can then be passed as an argument to other programs/scripts, saved to a file, or potentially even sent over the internet in various ways. The receiver of the xml can convert the CliXml back to the corresponding objects.

But, in your scenario, your best choice for using CliXml is to use Export-Clixml and Import-Clixml.

The following script saves your registry value to a file with the extension "clixml", but you can use any extension you want. Note, -Depth 1000 isn't needed in your case, but becomes important if you are working with nested hash tables, PSCustomObjects, Arrays, Collections, etc... But, you will likely want to use -Force to "Forces the command to run without asking for user confirmation":

$renames = (Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\Session Manager").PendingFileRenameOperations
Export-Clixml -Depth 1000 -Path 'C:\temp\renames.clixml' -InputObject $renames -Force

This following script restores the variable's content by reading the previously saved CliXml and converting to a string array. Note, without [string[]] the results are an [ArrayList], so, if you want the exact original object type, you may in some cases have to force it to be what you want:

[string[]]$renames = Import-Clixml -Path 'C:\temp\renames.clixml'
$renames