Powershell -使用数组将哈希表写入文件

omjgkv6w  于 2023-01-30  发布在  Shell
关注(0)|答案(2)|浏览(153)

我想将一个哈希表写出到一个文件中,其中一个哈希表项是数组。我的数组项已写出,但它包含files=System.Object[]

  • 注意--一旦这起作用,我将反向执行此过程,并再次读取哈希表。*
clear-host
$resumeFile="c:\users\paul\resume.log"
$files = Get-ChildItem *.txt
$files.GetType()
write-host
$types="txt"
$in="c:\users\paul"

Remove-Item $resumeFile -ErrorAction SilentlyContinue
$resumeParms=@{}
$resumeParms['types']=$types
$resumeParms['in']=($in)
$resumeParms['files']=($files)
$resumeParms.GetEnumerator() | ForEach-Object {"{0}={1}" -f $_.Name,$_.Value} | Set-Content $resumeFile
write-host "Contents of $resumefile"
get-content $resumeFile

结果

IsPublic IsSerial Name                                     BaseType                                                      
-------- -------- ----                                     --------                                                      
True     True     Object[]                                 System.Array                                                  

Contents of c:\users\paul\resume.log
files=System.Object[]
types=txt
in=c:\users\paul
cygmwpex

cygmwpex1#

立即解决的方法是创建自己的数组表示,方法是枚举元素并用,分隔它们,并将字符串值包含在'...'中:

# Sample input hashtable. [ordered] preserves the entry order.
$resumeParms = [ordered] @{ foo = 42; bar = 'baz'; arr = (Get-ChildItem *.txt) }

$resumeParms.GetEnumerator() |
  ForEach-Object { 
    "{0}={1}" -f $_.Name, (
      $_.Value.ForEach({ 
       (("'{0}'" -f ($_ -replace "'", "''")), $_)[$_.GetType().IsPrimitive] 
      }) -join ','
    )
  }

这并不是说所有的非基本.NET类型都用.ToString()表示法表示为 * string *,这可能不够好,也可能不够好。
上面的输出类似于:

foo=42
bar='baz'
arr='C:\Users\jdoe\file1.txt','C:\Users\jdoe\file2.txt','C:\Users\jdoe\file3.txt'

下面一节介绍了创建*.psd1文件的变体,该文件可以在以后用Import-PowerShellDataFile读回到哈希表示例中。

在文本文件中保存设置/配置数据的替代方法:

  • 如果您不介意依赖第三方模块
  • 考虑使用PSIni模块,该模块使用Windows初始化文件(*.ini)文件格式;参见X1 E2 F1 X的使用示例。
  • GitHub issue #9035中建议将对初始化文件的支持添加到PowerShell本身(7.0中不存在)。
  • 考虑使用**YAML**作为文件格式;例如经由X1 E5 F1 X模块。
  • GitHub issue #3607中建议将对YAML文件的支持添加到PowerShell本身(7.0中不存在)。
  • Configuration模块基于持久化的PowerShell * 哈希表文本*提供写入和读取***.psd1文件**的命令,就像您在源代码中声明它们一样。
  • 或者,您可以修改顶部代码中的输出格式,自己生成这样的文件,这样您就可以通过

Import-PowerShellDataFile,如底部所示。

  • 从PowerShell7.0开始,没有内置的对 * 写入 *(如表示)的支持;也就是说,没有补充的Export-PowerShellDataFile cmdlet。然而,在GitHub issue #11300中提出了添加该能力。
  • 如果创建(主要是)* 纯文本 * 文件 * 不是 * 必须
  • 就其支持的数据类型而言,提供最大灵活性的解决方案是Export-Clixml创建的基于XML的CLIXML格式,正如Lee Dailey建议的那样,其输出可以在以后使用Import-Clixml读取。

然而,这种格式在 type fidelity 方面也有限制,如this answer中所述。

  • 保存数据的**JSON表示,正如Lee还建议的那样,通过ConvertTo-Json/ConvertFrom-Json,是另一种选择,它使得输出比XML更人性化,但仍然不如纯文本表示**友好;值得注意的是,文件路径中的所有\字符必须在JSON中转义为\\

写入可使用Import-PowerShellDataFile读取的*.psd1文件

在所述的数据类型约束范围内(本质上,任何不是数字或字符串的内容都将成为 string),修改顶部的代码将PowerShell散列表文字表示写入*.psd1文件,以便可以通过Import-PowerShellDataFile**将其作为[hashtable]示例读回,这是相当容易的:
如前所述,如果您不介意安装模块,可以考虑Configuration模块,它内置了此功能。

# Sample input hashtable.
$resumeParms = [ordered] @{ foo = 42; bar = 'baz'; arr = (Get-ChildItem *.txt) }

# Create a hashtable-literal representation and save it to file settings.psd1
@"
@{
$(
  ($resumeParms.GetEnumerator() |
    ForEach-Object { 
      "  {0}={1}" -f $_.Name, (
        $_.Value.ForEach({ 
          (("'{0}'" -f ($_ -replace "'", "''")), $_)[$_.GetType().IsPrimitive] 
         }) -join ','
      )
    }
  ) -join "`n"
)
}
"@ > settings.psd1

如果稍后使用Import-PowerShellDataFile settings.psd1读取settings.psd1,则会得到一个[hashtable]示例,可以像往常一样访问该示例的条目,并生成以下显示输出:

Name                           Value
----                           -----
bar                            baz
arr                            {C:\Users\jdoe\file1.txt, C:\Users\jdoe\file1.txt, C:\Users\jdoe\file1.txt}
foo                            42

注意条目(键)的顺序是如何被 * 不 * 保留的,因为哈希表条目本质上是无序的。
在 * 写入 * *.psd1文件时,您可以通过将输入哈希表(System.Collections.Hashtable)声明为[ordered]来保留键(-creation)顺序,如上所示(这将创建一个System.Collections.Specialized.OrderedDictionary示例),但不幸的是,在 * 阅读 * *.psd1文件时,该顺序丢失了。
从PowerShell 7.0开始,即使您将[ordered]放在*.psd1文件 * 中的@{ * 之前,Import-PowerShellDataFile也会悄悄地忽略它,并创建一个 * 无序 * 哈希表。

vmdwslir

vmdwslir2#

这是一个我一直在处理的问题,它让我抓狂。我真的认为应该有一个专门针对这个动作的函数...所以我写了一个。

function ConvertHashTo-CSV
{

Param (
    [Parameter(Mandatory=$true)]
    $hashtable, 
    [Parameter(Mandatory=$true)]
    $OutputFileLocation
    )

$hastableAverage = $NULL #This will only work for hashtables where each entry is consistent. This checks for consistency.
foreach ($hashtabl in $hashtable)
{
    $hastableAverage = $hastableAverage + $hashtabl.count #Counts the amount of headings.
}

$Paritycheck = $hastableAverage / $hashtable.count #Gets the average amount of headings

if ( ($parity = $Paritycheck -is [int]) -eq $False) #if the average is not an int the hashtable is not consistent
    { 
    write-host "Error. Hashtable is inconsistent" -ForegroundColor red
    Start-Sleep -Seconds 5
    return
    }

$HashTableHeadings = $hashtable[0].GetEnumerator().name #Get the hashtable headings 
$HashTableCount = ($hashtable[0].GetEnumerator().name).count #Count the headings

$HashTableString = $null # Strange to hold the CSV

foreach ($HashTableHeading in $HashTableHeadings) #Creates the first row containing the column headings
{
    $HashTableString += $HashTableHeading
    $HashTableString += ", "
}

$HashTableString = $HashTableString -replace ".{2}$" #Removed the last , added by the above loop in error

$HashTableString += "`n"

foreach ($hashtabl in $hashtable) #Adds the data
{

    for($i=0;$i -lt $HashTableCount;$i++)
        {
        $HashTableString += $hashtabl[$i]
            if ($i -lt ($HashTableCount - 1))
                {
                $HashTableString += ", " 
                }       
        }
    $HashTableString += "`n"
}

$HashTableString | Out-File -FilePath $OutputFileLocation #writes the CSV to a file

}

要使用此函数,请将函数复制到脚本中,运行它,然后

ConvertHashTo-CSV -$hashtable $Hasharray -$OutputFileLocation c:\temp\data.CSV

代码带有注解,但对它的功能进行了简要说明。逐步通过数组和哈希表,并将它们添加到字符串中,添加所需的格式,使字符串成为CSV文件,然后将其输出到文件。
这个方法的主要限制是数组中的哈希表必须包含相同数量的字段,如果哈希表中有一个字段不包含数据,要避免这个问题,请确保它至少包含一个空格。
更多关于这一点可以在这里找到:https://grumpy.tech/powershell-convert-hashtable-to-csv/

相关问题