Using Powershell environmental variables as strings when outputting files

三世轮回 提交于 2021-02-19 18:50:43

问题


I am using Get-WindowsAutopilotInfo to generate a computer's serial number and a hash code and export that info as a CSV.

Here is the code I usually use:

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

This outputs a CSV file named "Autopilot_CSV.csv" to the C:\Autopilot_Export directory and then Robocopy copies it to the Network Share drive inside of the Hash_Exports folder listed above. In the above code, if I manually type in "test", "123", "ABC", etc. and replace "Autopilot_CSV" it will output a CSV under all of those names as well. So it looks like Get-WindowsAutopilotInfo will create a CSV file and save the file name with whatever string you pass into it. Great.

However, I am running this script on multiple different computers and I would like the exported CSV to be named something unique to the machine it is running on so I can easily identify it once it's copied. I am trying to pass the value of the environmental variable $env:computername as a string input for the CSV and it isn't working.

Here's the code I'm trying to use:

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

This code does not generate the CSV file at all. Why not?

Do I just have a basic misunderstanding of how environmental variables are used in Powershell? $env:computername appears to return a string of the computer's name, but I cannot pass it into Get-WindowsAutopilotInfo and have it save, but it will work if I manually type a string in as the input.

I have also tried setting it to a variable $computername = [string]$env:computername and just passing $computername in before the .CSV and that doesn't appear to work either. And per the docmentation, environmental variables are apparently already strings.

Am I missing something?

Thanks!


回答1:


Doublequoting seems to work. The colon is special in powershell parameters.

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

The colon is special. For example in switch parameters:

get-childitem -force:$true

Actually, it's trying to find a property named 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



回答2:


js2010's answer shows the effective solution: use "..."-quoting, i.e. an expandable string explicitly.

It is a good habit to form to use "..." explicitly around command arguments that are strings containing variable references (e.g. "$HOME/projects") or subexpressions (e.g., "./folder/$(Get-Date -Format yyyy-MM)")

While such compound string arguments generally do not require double-quoting[1] - because they are implicitly treated as if they were "..."-enclosed - in certain cases they do, and when they do is not obvious and therefore hard to remember:

This answer details the surprisingly complex rules, but here are two notable pitfalls:

  • If a compound argument starts with a subexpression ($(...)), its output is passed as a separate argument; e.g. Write-Output $(Get-Location)/folder passes two arguments to Write-Output: the result of $(Get-Location) and literal /folder

  • If a compound argument starts with a variable reference and is followed by what syntactically looks like either (a) a property access (e.g., $PSVersionTable.PsVersion) or (b) a method call (e.g., $PSHome.Tolower()) it is executed as just that, i.e. as an expression (rather than being considered a variable reference followed by a literal part).

    • Aside #1: Such an argument then isn't necessarily a string, but whatever data type the property value or method-call return value happens to be.

    • Aside #2: A compound string that starts with a quoted string (whether single- or double-quoted) does not work, because the quoted string at the start is also considered an expression, like property access and method calls, so that what comes after is again passed as separate argument(s). Therefore, you can only have a compound strings consisting of a mix of quoted and unquoted substrings if the compound string starts with an unquoted substring or a non-expression variable reference.[2]

The latter is what happened in this case:

  • Unquoted $env:computername.csv was interpreted as an attempt to access a property named csv on the object stored in (environment) variable $env:computername, and since the [string] instance stored there has no csv property, the expression evaluated to $null.

  • By forcing PowerShell to interpret this value as an expandable string via "...", the usual interpolation rules apply, which means that the .csv is indeed treated as a literal (because property access requires use of $(...) in expandable strings).


[1] Quoting is required for strings that contain metacharacters such as spaces.
For values to be treated verbatim, '...' quoting is the better choice (see the bottom section of this answer for an overview of PowerShell string literals).
Also, using neither double- nor single-quoting and individually `-escaping metacharacters is another option (e.g. Write-Output C:\Program` Files.

[2] For instance, Write-Output a"b`t"'`c' and Write-Output $HOME"b`t"'`c' work as intended, whereas Write-Output "b`t"'`c' and Write-Output $HOME.Length"b`t"'`c' do not (the latter two each pass 3 arguments). The workaround is to either use a single "..." string with internal `-escaping as necessary (e.g. Write-Output "${HOME}b`t``c") or to use string-concatenation expression with + enclosed in (...) (e.g. Write-Output ($HOME + "b`t" + '`c'))




回答3:


Basically pass it a string rather than the variable:

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

Try the following syntax:

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

# output: MYPCNAME.csv


来源:https://stackoverflow.com/questions/66160137/using-powershell-environmental-variables-as-strings-when-outputting-files

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!