问题描述
我正在使用 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
我是否只是对 Powershell 中如何使用环境变量有基本的误解? $env:computername
似乎返回计算机名称的字符串,但我无法将其传递到 Get-WindowsAutopilotInfo 并保存它,但是如果我手动输入字符串作为输入,它将工作.
我还尝试将其设置为变量 $computername = [string]$env:computername
并仅在 .CSV 之前传入 $computername
并且这似乎也不起作用。根据文档,环境变量显然已经是字符串了。
我错过了什么吗?
谢谢!
解决方法
js2010's answer 显示了有效的解决方案:使用 "..."
-quoting,即 expandable string explicitly。
在命令参数周围使用"..."
显式是一个好习惯,这些命令参数是包含变量的字符串引用(例如 "$HOME/projects"
)或子表达式(例如 "./folder/$(Get-Date -Format yyyy-MM)"
)
虽然这样的复合字符串参数一般需要双引号[1] - 因为它们隐式 被视为被 "..."
封闭 - 在某些情况下,他们做,并且何时他们做的并不明显,因此很难记住:
This answer 详细说明了令人惊讶的复杂规则,但这里有两个值得注意的陷阱:
-
如果复合参数以子表达式(
的结果$(...)
)开头,则其输出将作为单独的参数传递;例如Write-Output $(Get-Location)/folder
将 两个 参数传递给Write-Output
:$(Get-Location)
和文字/folder
-
如果一个复合参数以一个变量引用和后面跟着语法上的样子要么 (a) a 属性访问(例如,
$PSVersionTable.PsVersion
)或(b)一个方法调用(例如,$PSHome.Tolower()
)它是执行的,即作为表达式(而不是被视为变量引用后跟文字部分)。-
旁白#1:这样的参数不一定是字符串,而是属性值或方法调用返回值碰巧是什么数据类型。
-
旁白#2:复合字符串以带引号的字符串(无论是单引号还是双引号)不 > 工作,因为开头的引用字符串也被认为是一个表达式,就像属性访问和方法调用一样,因此后面的内容再次作为单独的参数传递。因此,如果复合字符串以 unquoted 子字符串或非表达式变量引用开头,则您只能拥有由带引号和不带引号的子字符串混合组成的复合字符串。[2]上>
-
后者是在这种情况下发生了什么:
-
未加引号
$env:computername.csv
被解释为尝试访问存储在 (环境)变量csv
,并且由于存储在那里的$env:computername
实例没有[string]
属性,因此表达式计算为csv
。 -
通过强制 PowerShell 将此值解释为通过
$null
的可扩展字符串,通常的 interpolation rules 适用,这意味着"..."
是确实被视为文字(因为属性访问需要在可扩展字符串中使用.csv
)。
[1] 包含元字符(例如空格)的字符串需要引用。
对于要逐字处理的值,$(...)
引用是更好的选择(有关 PowerShell 字符串文字的概述,请参阅 this answer 的底部部分)。
此外,既不使用双引号也不使用单引号和单独 '...'
转义元字符是另一种选择(例如 `
.
[2] 例如,Write-Output C:\Program` Files
和 Write-Output a"b`t"'`c'
按预期工作,而 Write-Output $HOME"b`t"'`c'
和 Write-Output "b`t"'`c'
则不然(后两个都通过 3 个参数)。解决方法是根据需要使用带有内部 Write-Output $HOME.Length"b`t"'`c'
转义的 single "..."
字符串(例如 `
),或者使用带有 {{ 1}} 括在 Write-Output "${HOME}b`t``c"
中(例如 +
)
双引号似乎有效。冒号在 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