Loading a PowerShell hashtable from a file? - powershell

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.

(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)

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

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

If you can give this file the extension .ps1, say, data.ps1 then it cannot be simpler than this code:
$data = <path>\data.ps1

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.

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

Related

Change text in file with data from an other file and write out to screen

I would start with I was looking for answers but I could not find one.
The code is this but it reads in 1 line from the data.txt and after that all lines in message.txt.
The message.txt contains strings and data.txt has these separeted with ';' like this:
Josh Adams;NYC 5th Avenue;18:25
Peter Nordmann;NCY 5th Avenue;20:00
...
foreach ($dataLine in Get-Content .\data.txt){
$data = $dataLine -split ';'
foreach($line in Get-Content .\message.txt){
$line.replace('<name>', $data[0])
$line.replace('<place>', $data[1])
$line.replace('<time>', $data[2])
}
Write-Output $line
}
Just capture your searched text content to a variable, use that variable content to write to the new or the other file, using the normal built-in cmdlet(s)...
Add-Content
or
Out-File
.. using the append options.
Lastly, there are multiple ways to save content to a variable, for use elsewhere and output to the screen.
3 ways to store and display PowerShell Variable simultaneously
# Using -OutVariable parameter
Get-Process a* -OutVariable process
# PowerShell Variable squeezing
($process = Get-Process a*)
# Using Tee-Object Cmdlet
Get-Process a* | Tee-Object -Variable p

Retrieve integers from a text file using PowerShell

I would like to retrieve some numbers from a text file using Windows PowerShell. Assume that I have a text file values.txt that looks like this (I can modify it as I want):
foo=100
bar=-3
foobar=-15
asdf=-4
I would like to add a variable to my PowerShell script called $bar that is equal to the number -3, as the text file says it should be. How do I do this?
Note: The input file looks like part of an *.ini file; for more full-featured support of such files, consider a third-party module such as PSIni.
As Matt suggests in a comment on the question, ConvertFrom-StringData sounds like the right tool:
# Read the key-value pairs stored in file file.txt into [hashtable] $ht
PS> ($ht = Get-Content -Raw -LiteralPath file.txt | ConvertFrom-StringData)
Name Value
---- -----
foo 100
bar -3
foobar -15
asdf -4
# Access a specific value:
PS> $ht.foo # or: $ht['foo']
100
Note:
ConvertFrom-StringData returns a hashtable rather than creating individual variables:
As shown above, you must access the foo input line's value as $ht.foo (or $ht['foo']) rather than $foo, for instance.
To ensure that only a single hashtable is created, Get-Content -Raw (PSv3+) must be used to pass the entire input file as a single string.
ConvertFrom-StringData only ever creates [string] values, so if the input values should be treated as numbers, for instance, manual conversion is required:
# .Clone() is needed to support enumeration and modification in the same loop.
foreach($key in $ht.Keys.Clone()) { $ht.$key = [int] $ht.$key }
Generally, there are more subtleties to consider, such as \ rather than ` serving as the escape character and quotes getting retained as literals - see the docs.
This should work:
$file = "C:\test.txt"
foreach($line in (Get-Content $file)) {
$a = $line.Split("=")
New-Variable -Name $a[0] -Value $a[1]
}
Write-Output ""
Write-Output "Variable Check ::"
Write-Output "foo = $foo"
Write-Output "bar = $bar"
Write-Output "foobar = $foobar"
Write-Output "asdf = $asdf"
You can also put the name/value in different variables if you don't want to use an array, just change the for loop:
$a,$b = $line.Split("=")
New-Variable -Name $a -Value $b
Using Matts suggestion of ConvertFrom-StringData :
$line = $line | ConvertFrom-StringData
New-Variable -name $line.Keys -Value $line.Values
hope this helps

Output PowerShell variables to a text file

I'm new to PowerShell and have a script which loops through Active Directory searching for certain computers. I get several variables and then run functions to check things like WMI and registry settings.
In the console, my script runs great and simple Write-Host command prints the data on the screen as I want. I know about Export-Csv when using the pipeline...but I'm not looking to print from the pipeline.
I want to write the variables to a text file, continue the loop, and check the next computer in AD...output the next iteration of the same variables on the next line. Here is my Write-Host:
Write-Host ($computer)","($Speed)","($Regcheck)","($OU)
Output file:
$computer,$Speed,$Regcheck | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
It gives me the data, but each variable is on its own line. Why? I'd like all the variables on one line with comma separation. Is there a simple way to do this akin to VB writeline? My PowerShell version appears to be 2.0.
Use this:
"$computer, $Speed, $Regcheck" | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
I usually construct custom objects in these loops, and then add these objects to an array that I can easily manipulate, sort, export to CSV, etc.:
# Construct an out-array to use for data export
$OutArray = #()
# The computer loop you already have
foreach ($server in $serverlist)
{
# Construct an object
$myobj = "" | Select "computer", "Speed", "Regcheck"
# Fill the object
$myobj.computer = $computer
$myobj.speed = $speed
$myobj.regcheck = $regcheck
# Add the object to the out-array
$outarray += $myobj
# Wipe the object just to be sure
$myobj = $null
}
# After the loop, export the array to CSV
$outarray | export-csv "somefile.csv"
You can concatenate an array of values together using PowerShell's `-join' operator. Here is an example:
$FilePath = '{0}\temp\scripts\pshell\dump.txt' -f $env:SystemDrive;
$Computer = 'pc1';
$Speed = 9001;
$RegCheck = $true;
$Computer,$Speed,$RegCheck -join ',' | Out-File -FilePath $FilePath -Append -Width 200;
Output
pc1,9001,True
$computer,$Speed,$Regcheck will create an array, and run out-file ones per variable = they get seperate lines. If you construct a single string using the variables first, it will show up a single line. Like this:
"$computer,$Speed,$Regcheck" | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
The simple solution is to avoid creating an array before piping to Out-File. Rule #1 of PowerShell is that the comma is a special delimiter, and the default behavior is to create an array. Concatenation is done like this.
$computer + "," + $Speed + "," + $Regcheck | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
This creates an array of three items.
$computer,$Speed,$Regcheck
FYKJ
100
YES
vs. concatenation of three items separated by commas.
$computer + "," + $Speed + "," + $Regcheck
FYKJ,100,YES
I was lead here in my Google searching. In a show of good faith I have included what I pieced together from parts of this code and other code I've gathered along the way.
# This script is useful if you have attributes or properties that span across several commandlets
# and you wish to export a certain data set but all of the properties you wish to export are not
# included in only one commandlet so you must use more than one to export the data set you want
#
# Created: Joshua Biddle 08/24/2017
# Edited: Joshua Biddle 08/24/2017
#
$A = Get-ADGroupMember "YourGroupName"
# Construct an out-array to use for data export
$Results = #()
foreach ($B in $A)
{
# Construct an object
$myobj = Get-ADuser $B.samAccountName -Properties ScriptPath,Office
# Fill the object
$Properties = #{
samAccountName = $myobj.samAccountName
Name = $myobj.Name
Office = $myobj.Office
ScriptPath = $myobj.ScriptPath
}
# Add the object to the out-array
$Results += New-Object psobject -Property $Properties
# Wipe the object just to be sure
$myobj = $null
}
# After the loop, export the array to CSV
$Results | Select "samAccountName", "Name", "Office", "ScriptPath" | Export-CSV "C:\Temp\YourData.csv"
Cheers

Powershell retrieving a variable from a text file

Is there a way to read a text file C:\test.txt and retrieve a particular value?
ie file looks like this:
serverName=serv8496
midasServer=serv8194
I want to set the value of a variable in my script in some way from this file eg:
$MidasServer= (from file midasServer value)
I will not know the line number where the reference is.
Any way to do this?
Yes, read the file, split each line and assign the split result to the Name and Value parameters:
Get-Content file.txt | Foreach-Object{
$var = $_.Split('=')
New-Variable -Name $var[0] -Value $var[1]
}
If that is exactly how your file appears i.e. a list of key value pairs denoted with a equals sign then you should have a look at ConvertFrom-StringData which
converts a string that contains one or more key and value pairs into a hash table. Because each key/value pair must be on a separate line, here-strings are often used as the input format.
So if a text file contained just the data in your example you could do this to create a hashtable
$Path = "C:\temp\test.txt"
$values = Get-Content $Path | Out-String | ConvertFrom-StringData
$values.midasServer
Where the $values.midasServer would have the value serv8194. No need to know where the properties are in respect to the file. Your input file can also have varying leading and trailing space around the equals sign which will give the exact same result.
Depending on your use case you can take that one step farther and create a custom object from that hashtable
New-Object -TypeName pscustomobject -Property $values
If you have at least PowerShell v3 or higher you can simplify the process (assuming you want a custom psobject)
$values = [pscustomobject](Get-Content $Path -Raw | ConvertFrom-StringData)
$values.midasServer
This is an improvement to the Shay Levy's answer. It does the following.
It ignores commented lines and new lines in the file.txt before
start processing the file. So it resolves the error saying that name
could not be created because it is an empty string.
It splits only on the first occurrence of the character "=".
Therefore you can use any characters in the value field.
It performs Trim() operation in order to remove space characters from the beginning and end of the variable/property. Therefore "VARIABLE=VALUE" and "VARIABLE = VALUE" in the file.txt returns the same.
Set the scope of new variables to "Script". Variables created in the
script scope are accessible only within the script file or module
they are created in. Other options are Global, Local and Private.
You can find a variable scope reference here.
Get-Content file.txt | Where-Object {$_.length -gt 0} | Where-Object {!$_.StartsWith("#")} | ForEach-Object {
$var = $_.Split('=',2).Trim()
New-Variable -Scope Script -Name $var[0] -Value $var[1]
}
This was successful for me:
(input file = filename.txt)
[string] $person 'Joe'
[int] $age 50
[datetime] $dob '06/11/1971'
(commands)
Get-Content filename.txt | ForEach-Object {
$invar = $_.Split(" ").Trim()
Invoke-Expression (Invoke-Command -ScriptBlock {
$($invar[0])+$($invar[1])+'='+$($invar[2])
} )
}
If you know there's a "value", that will contain spaces, i.e. "person "Joe B", just join the values, like so...
Replace this:
        $($invar[0])+$($invar[1])+'='+$($invar[2])
With this:
        $($invar[1])+$($invar[2])+'='+ (-join $($invar[3]),$($invar[4]),$($invar[5]) )

Powershell: modifying text

ok, I give up! why doesn't this work? Im just trying to loop through a csv file and replace any value in the nth column with some value.
$source = "C:\blah.csv"
(gc $source) | foreach{ $_.Split(',')[10] = 'something'} | sc $source
Basically, what you are trying is something like that:
$s = 'a,b,c,d,e'
$s.Split(',')[4] = 'something'
$s
That won't work, because you don't assign 'something' to any variable.
I would either read the file as csv (via Import-CSV) or (if it structure is really simple) use regexes:
$s = 'c,d,e', 'c,d,x', 'z,f,d'
$s | % { $_ -replace '(?<=([^,]*,){2}).*','something'}