18

I've got a file containing some data in PowerShell Object Notation:

@{ X = 'x'; Y = 'y' }

I'd like to load this into a variable from the file.

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380
  • For an array of hashes, would each line be enclosed in @{ }, such as @{Z = 'z'; A='a' }? – rjt Apr 02 '19 at 14:49

6 Answers6

26

(I figured it out while putting together a repro)

PS> $content = ( Get-Content .\foo.pson | Out-String )
PS> $data = ( Invoke-Expression $content )

Get-Content returns an array with the lines in the file; the Out-String is used to join them together.

Invoke-Expression then runs the script, and the result is captured. This is open to injection attacks, but that's OK in my specific case.

Or, if you prefer your PowerShell terse:

PS> $data = gc .\foo.pson | Out-String | iex

(I can't find a shorter form of Out-String)

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380
  • Maybe this is a Powershell 4 thing but I am able to skip the Out-String part and simply do: $hash = Get-Content .\file.pson | Invoke-Expression – Emil G Sep 09 '14 at 12:57
12

I've used ConvertFrom-StringData. If you want to use this approach you'll need to change the way you store key/value pairs with each on its own line and no quotes:

#Contents of test.txt
X = x
Y = y

get-content .\test.txt | ConvertFrom-StringData

Name                           Value
----                           -----
X                              x
Y                              y

ConvertFrom-StringData is a built-in cmdlet. I created corresponding ConvertTo-StringData function available here http://poshcode.org/1986

Chad Miller
  • 40,127
  • 3
  • 30
  • 34
  • I didn't mention it in the question (to keep things simple), but the file actually contains an object tree: an array of hashtables, containing other arrays and hashtables. – Roger Lipscombe May 25 '12 at 14:30
6

I ran into trouble using ConvertFrom-StringData as @Chad suggested. If you do:

$hash = get-content .\test.txt | ConvertFrom-StringData

I found I had an object array rather than a hash table. In fact, it appears that I had an array of hash tables, each with one entry. I confirmed with a:

$hash.GetType()

It looks like you need to join each line of the slurped input file to ensure that it forms a single string for ConvertFrom..'s use:

$hash = ((get-content .\test.txt) -join '`n') | ConvertFrom-StringData
Tim Barrass
  • 4,813
  • 2
  • 29
  • 55
  • 7
    You can also just use `Get-Content -Raw .\test.txt` which stops the file being piped line by line to `ConvertFrom-StringData`; http://www.geekality.net/2014/06/30/powershell-read-hashtable-from-a-file/ – Dave Anderson Dec 08 '15 at 01:19
5

If you can give this file the extension .ps1, say, data.ps1 then it cannot be simpler than this code:

$data = <path>\data.ps1
Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
  • True (and I didn't realise that I didn't have to dot-source the file) , but then it looks like it can be run directly, which I don't particularly want. – Roger Lipscombe May 25 '12 at 05:53
4

Starting from PowerShell 5.0 you have

Import-PowerShellDataFile

Which imports values from a .psd1-file. So the only thing you have to do is rename your file to *.psd1

Official help is here.

Artem Bokov
  • 162
  • 1
  • 10
3

This is an older post but, this is sort of a twist on your accepted solution and perhaps slightly more "safe", keep in mind un-trusted files.

From your notes, you have a file that contains a hashtable using Powershell syntax. Given that constraint, you can import it directly:

$HashPath = ".\foo.pson"
# input file contents
$filecontent = Get-Content -Path $HashPath -Raw -ErrorAction Stop
# put the file in a script block
$scriptBlock = [scriptblock]::Create( $filecontent )
#check that the file contains no other Powershell commands
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
#execute it to create the hashtable 
$hashtable = ( & $scriptBlock ) 

Note on the $scriptBlock.CheckRestrictedLanguage you could replace that with

$scriptBlock.CheckRestrictedLanguage([string[]]@(), [string[]]@(), $false)

Use an empty list of strings so we do not allow any Powershell commands. When importing a hashtable, this is exactly what we want. That last one is allowEnvironmentVariables so we restrict that in this example with $false.

Side note, a Powershell module (psd1 file) is just a hashtable so this concept may help you to also pull in script blocks or other things.

Reference: https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.scriptblock.checkrestrictedlanguage?view=powershellsdk-1.1.0

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100