输出文件时使用Powershell环境变量作为字符串

Using Powershell environmental variables as strings when outputting files

我正在使用 Get-WindowsAutopilotInfo 生成计算机的序列号和哈希码,并将该信息导出为 CSV。

这是我常用的代码:

new-item "c:\Autopilot_Export" -type directory -force

Set-Location "C:\Autopilot_Export"

Get-WindowsAutopilotInfo.ps1 -OutputFile Autopilot_CSV.csv

Robocopy C:\Autopilot_Export \Zapp\pc\Hash_Exports /copyall

这会将名为“Autopilot_CSV.csv”的 CSV 文件输出到 C:\Autopilot_Export 目录,然后 Robocopy 将其复制到上面列出的 Hash_Exports 文件夹内的网络共享驱动器。在上面的代码中,如果我手动输入“test”、“123”、“ABC”等并替换“Autopilot_CSV”,它也会在所有这些名称下输出一个 CSV。所以看起来 Get-WindowsAutopilotInfo 将创建一个 CSV 文件并使用您传递给它的任何字符串保存文件名。太好了。

但是,我在多台不同的计算机上 运行 这个脚本,我希望导出的 CSV 被命名为它所在的机器独有的名称 运行 这样我就可以轻松识别它一次它被复制了。我正在尝试将环境变量 $env:computername 的值作为 CSV 的字符串输入传递,但它不起作用。

这是我尝试使用的代码:

new-item "c:\Autopilot_Export" -type directory -force

Set-Location "C:\Autopilot_Export"

Get-WindowsAutopilotInfo.ps1 -OutputFile $env:computername.csv

Robocopy C:\Autopilot_Export C:\Users\danieln\Desktop /copyall

此代码根本不生成 CSV 文件。为什么不呢?

我只是对 Powershell 中如何使用环境变量有基本的误解吗? $env:computername 似乎是 return 计算机名称的字符串,但我无法将其传递到 Get-WindowsAutopilotInfo 并保存它,但如果我手动 工作输入一个字符串作为输入。

我也试过将它设置为一个变量 $computername = [string]$env:computername 并只是在 .CSV 之前传递 $computername 并且这似乎也不起作用。根据文档,环境变量显然已经是字符串。

我是不是漏掉了什么?

谢谢!

双引号似乎有效。冒号在powershell参数中比较特殊

echo hi | set-content "$env:computername.csv"

dir


    Directory: C:\users\me\foo


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         2/11/2021   1:02 PM              4 COMP001.csv

冒号很特殊。例如在开关参数中:

get-childitem -force:$true

实际上,它试图找到一个 属性 名为 csv:

echo hi | Set-Content  $env:COMPUTERNAME.length
dir


    Directory: C:\Users\me\foo


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         2/11/2021   3:04 PM              4 8

基本上传递一个字符串而不是变量:

write-host $env:computername.csv
# output: (no output - looking for a variable called "computername.csv" instead of "computername")

尝试以下语法:

$filename = "$($env:computername).csv"
write-host $filename 

# output: MYPCNAME.csv

shows the effective solution: use "..."-quoting, i.e. an expandable string 明确.

命令参数周围使用"..."显式是一个好习惯字符串 包含 变量引用(例如 "$HOME/projects")或子表达式(例如 "./folder/$(Get-Date -Format yyyy-MM)"

虽然这样的复合字符串参数通常需要双引号[1] - 因为它们 隐含地 被视为 "..."-封闭 - 在某些情况下它们 dowhen 它们的作用并不明显,因此很难记住:

详细介绍了令人惊讶的复杂规则,但这里有 两个值得注意的陷阱:

  • 如果复合参数 子表达式 ($(...)) 开头,其输出将作为单独的参数传递;例如Write-Output $(Get-Location)/folder 两个 参数传递给 Write-Output$(Get-Location) 的结果和文字 /folder

  • 如果复合参数 开头,变量引用 and 后跟 语法上的内容看起来 (a) 属性 访问 (例如,$PSVersionTable.PsVersion)或 (b) 方法调用(例如,$PSHome.Tolower())它被 执行,即作为一个 表达式(而不是被认为是一个变量引用后跟文字部分)。

    • 旁白 #1:这样的参数不一定是 字符串 ,但无论数据类型是 属性 值还是方法调用return值恰好是。

    • 旁白#2:开头的复合字符串 带引号的字符串(无论是单引号还是双引号quoted) not 工作,因为开头的引用字符串也被认为是 expression,就像 属性 访问和方法调用一样,这样之后的内容再次作为单独的参数传递。因此,如果复合字符串以 unquoted 子字符串或非表达式变量引用开头,则只能拥有由带引号和不带引号的混合子字符串组成的复合字符串。 [2]

后者是本例中发生的事情:

  • Unquoted $env:computername.csv 被解释为尝试访问 a 属性 named csv 存储在(环境)变量 $env:computername 中的对象上,并且由于存储的 [string] 实例没有 csv 属性,表达式的计算结果为 $null.

  • 通过强制 PowerShell通过"..."将此值解释为可扩展字符串,通常的interpolation rules适用,这意味着.csv 确实被视为 文字 (因为 属性 访问需要在可扩展字符串中使用 $(...))。


[1] 引用 包含 元字符 的字符串所必需的,例如空格。
对于要处理的值 verbatim'...' 引号是更好的选择(有关 PowerShell 字符串文字的概述,请参阅 this answer 的底部)。
此外,既不使用双引号也不使用单引号和 单独 ` 转义元字符是另一种选择(例如 Write-Output C:\Program` Files.

[2] 例如,Write-Output a"b`t"'`c'Write-Output $HOME"b`t"'`c' 按预期工作,而 Write-Output "b`t"'`c'Write-Output $HOME.Length"b`t"'`c' 没有(后两者各传递 3 个参数)。解决方法是使用 单个 "..." 字符串,必要时使用内部 ` 转义(例如 Write-Output "${HOME}b`t``c")或使用字符串连接表达式+ 包含在 (...) 中(例如 Write-Output ($HOME + "b`t" + '`c')