IF 语句内函数内 Tee-Object 命令的 PowerShell 控制台输出
PowerShell console output from Tee-Object command inside function inside IF statement
考虑以下代码:
Function ShowSave-Log {
Param ([Parameter(Mandatory=$true)][String] $text)
$PSDefaultParameterValues=@{'Out-File:Encoding' = 'utf8'}
$date=[string](Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append
#Write-Host "$date $text"
}
Function Is-Installed {
Param ([parameter(Mandatory=$true)][String] $app_name, [String] $app_version)
$apps = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Select-Object DisplayName, DisplayVersion
$apps += Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-Object DisplayName, DisplayVersion
$apps = $apps.Where{$_.DisplayName -like "$app_name"}
if ($apps.Count -eq 0) {
ShowSave-Log "`'$app_name`' not found in the list of installed applications."
return $false
} else {
ShowSave-Log "`'$app_name`' is installed."
return $true
}
}
$LOG_FILE="$Env:TEMP\LOG.log"
if (Is-Installed "Notepad++ (64-bit x64)") {Write-Host "TRUE"}
我希望在 ShowSave-Log 函数中看到来自 Tee-Object 命令的消息,但它从未显示在终端中。我猜这是因为它是从 'if' 语句调用的。如何将 Tee-Object 输出到终端屏幕?它被保存到日志文件中。
BTW Write-Host 命令正确地将消息输出到终端。
我正在使用 PowerShell ISE、Visual Studio 代码和 PowerShell 终端。 PowerShell 版本 5.1
如果你把它想成
可能更容易理解
$result = Is-Installed "Notepad++ (64-bit x64)"
if ($result) {Write-Host "TRUE"}
很明显,结果不会随时输出到控制台。
您可能还误解了 return 的工作原理
ShowSave-Log "`'$app_name`' not found in the list of installed applications."
return $false
在功能上与
相同
ShowSave-Log "`'$app_name`' not found in the list of installed applications."
$false
return
你最好让你的函数 return 简单的 PowerShell 对象而不是人类可读的文本和真值。
function Get-InstalledApps {
param (
[parameter(Mandatory=$true)][string] $app_name,
[string] $app_version
)
$installPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name
}
并将用户的格式保留在脚本的顶层。
使用 DefaultDisplayPropertySet
属性 可能值得查看自定义类型。例如:
Update-TypeData -TypeName 'InstalledApp' -DefaultDisplayPropertySet 'DisplayName', 'DisplayVersion'
function Get-InstalledApps {
param (
[parameter(Mandatory=$true)][string] $app_name,
[string] $app_version
)
$installPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name | Add-Member -TypeName 'InstalledApp' -PassThru
}
或者在没有自定义类型的情况下,这种令人厌恶的单行代码:
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value ([System.Management.Automation.PSMemberInfo[]](New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet, ([string[]]('DisplayName', 'DisplayVersion')))) -PassThru
Approved Verbs for PowerShell 页面也值得一看。
对于 Powershell 如何处理 return 数据存在一个常见的误解。实际上,没有您习惯于使用其他编程语言的 single return 值或对象。相反,有对象的 输出流 。
有几种方法可以将数据添加到输出流,例如。 g.:
Write-Output $data
$data
return $data
来自其他语言的 PS 新手感到困惑的是 return $data
没有定义函数的唯一“return 值”.这只是将 Write-Output $data
与函数提前退出相结合的一种便捷方式。在 return
语句之前写入输出流的任何数据也会影响函数的输出!
代码解析
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append
... 将 InputObject 附加到 ShowSave-Log
的输出流
ShowSave-Log "`'$app_name`' is installed."
... 将消息附加到 Is-Installed
的输出流
return $true
... 将值 $true
附加到 Is-Installed
的输出流
现在 Is-Installed
的输出流中实际上有 两个 个对象,字符串消息和 $true
值!
if (Is-Installed "Notepad++ (64-bit x64)") {Write-Host "TRUE"}
让我拆分 if 语句来详细解释它的作用:
$temp = Is-Installed "Notepad++ (64-bit x64)"
... 将 Is-Installed
的输出流重定向到临时变量。由于输出流已存储到一个变量中,它不会在函数调用链中进一步向上移动,因此它不会再显示在控制台中!这就是为什么您看不到来自 Tee-Object
.
的消息的原因
在我们的例子中,输出流中有多个对象,因此变量将是一个数组,如 @('... is installed', $true)
if ($temp) {Write-Host "TRUE"}
... 对数组 $temp
进行隐式布尔转换。非空数组转换为 $true
。所以这里有一个错误,因为函数 Is-Installed
总是 "returns" 一个非空数组。未安装软件时,$temp
看起来像 @('... not found ...', $false)
,它也会转换为 $true
!
证明:
$temp = Is-Installed "nothing"
$temp.GetType().Name # Prints 'Object[]'
$temp[0] # Prints '2020.12.13 12:39:37 'nothing' not found ...'
$temp[1] # Prints 'False'
if( $temp ) {'Yes'} # Prints 'Yes' !!!
如何将 Tee-Object 输出到终端屏幕?
不要让它写入输出流,输出流应该只用于从函数“returned”的实际数据,而不用于日志消息。
一个简单的方法是将 Tee-Object
的输出重定向到 Write-Host
,它写入信息流:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Host
更明智的方法是重定向到详细流:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Verbose
现在默认情况下,日志消息不会使终端混乱。相反,要查看详细的日志记录,调用者必须启用详细输出,例如。 G。通过设置 $VerbosePreference = 'Continue'
或使用 -Verbose
参数调用函数:
if( Is-Installed 'foo' -Verbose ){<# do something #>}
考虑以下代码:
Function ShowSave-Log {
Param ([Parameter(Mandatory=$true)][String] $text)
$PSDefaultParameterValues=@{'Out-File:Encoding' = 'utf8'}
$date=[string](Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append
#Write-Host "$date $text"
}
Function Is-Installed {
Param ([parameter(Mandatory=$true)][String] $app_name, [String] $app_version)
$apps = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Select-Object DisplayName, DisplayVersion
$apps += Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-Object DisplayName, DisplayVersion
$apps = $apps.Where{$_.DisplayName -like "$app_name"}
if ($apps.Count -eq 0) {
ShowSave-Log "`'$app_name`' not found in the list of installed applications."
return $false
} else {
ShowSave-Log "`'$app_name`' is installed."
return $true
}
}
$LOG_FILE="$Env:TEMP\LOG.log"
if (Is-Installed "Notepad++ (64-bit x64)") {Write-Host "TRUE"}
我希望在 ShowSave-Log 函数中看到来自 Tee-Object 命令的消息,但它从未显示在终端中。我猜这是因为它是从 'if' 语句调用的。如何将 Tee-Object 输出到终端屏幕?它被保存到日志文件中。 BTW Write-Host 命令正确地将消息输出到终端。 我正在使用 PowerShell ISE、Visual Studio 代码和 PowerShell 终端。 PowerShell 版本 5.1
如果你把它想成
可能更容易理解$result = Is-Installed "Notepad++ (64-bit x64)"
if ($result) {Write-Host "TRUE"}
很明显,结果不会随时输出到控制台。
您可能还误解了 return 的工作原理
ShowSave-Log "`'$app_name`' not found in the list of installed applications."
return $false
在功能上与
相同 ShowSave-Log "`'$app_name`' not found in the list of installed applications."
$false
return
你最好让你的函数 return 简单的 PowerShell 对象而不是人类可读的文本和真值。
function Get-InstalledApps {
param (
[parameter(Mandatory=$true)][string] $app_name,
[string] $app_version
)
$installPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name
}
并将用户的格式保留在脚本的顶层。
使用 DefaultDisplayPropertySet
属性 可能值得查看自定义类型。例如:
Update-TypeData -TypeName 'InstalledApp' -DefaultDisplayPropertySet 'DisplayName', 'DisplayVersion'
function Get-InstalledApps {
param (
[parameter(Mandatory=$true)][string] $app_name,
[string] $app_version
)
$installPaths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name | Add-Member -TypeName 'InstalledApp' -PassThru
}
或者在没有自定义类型的情况下,这种令人厌恶的单行代码:
Get-ItemProperty -Path $installPaths | Where-Object DisplayName -like $app_name | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value ([System.Management.Automation.PSMemberInfo[]](New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet, ([string[]]('DisplayName', 'DisplayVersion')))) -PassThru
Approved Verbs for PowerShell 页面也值得一看。
对于 Powershell 如何处理 return 数据存在一个常见的误解。实际上,没有您习惯于使用其他编程语言的 single return 值或对象。相反,有对象的 输出流 。
有几种方法可以将数据添加到输出流,例如。 g.:
Write-Output $data
$data
return $data
来自其他语言的 PS 新手感到困惑的是 return $data
没有定义函数的唯一“return 值”.这只是将 Write-Output $data
与函数提前退出相结合的一种便捷方式。在 return
语句之前写入输出流的任何数据也会影响函数的输出!
代码解析
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append
... 将 InputObject 附加到 ShowSave-Log
ShowSave-Log "`'$app_name`' is installed."
... 将消息附加到 Is-Installed
return $true
... 将值 $true
附加到 Is-Installed
现在 Is-Installed
的输出流中实际上有 两个 个对象,字符串消息和 $true
值!
if (Is-Installed "Notepad++ (64-bit x64)") {Write-Host "TRUE"}
让我拆分 if 语句来详细解释它的作用:
$temp = Is-Installed "Notepad++ (64-bit x64)"
... 将 Is-Installed
的输出流重定向到临时变量。由于输出流已存储到一个变量中,它不会在函数调用链中进一步向上移动,因此它不会再显示在控制台中!这就是为什么您看不到来自 Tee-Object
.
在我们的例子中,输出流中有多个对象,因此变量将是一个数组,如 @('... is installed', $true)
if ($temp) {Write-Host "TRUE"}
... 对数组 $temp
进行隐式布尔转换。非空数组转换为 $true
。所以这里有一个错误,因为函数 Is-Installed
总是 "returns" 一个非空数组。未安装软件时,$temp
看起来像 @('... not found ...', $false)
,它也会转换为 $true
!
证明:
$temp = Is-Installed "nothing"
$temp.GetType().Name # Prints 'Object[]'
$temp[0] # Prints '2020.12.13 12:39:37 'nothing' not found ...'
$temp[1] # Prints 'False'
if( $temp ) {'Yes'} # Prints 'Yes' !!!
如何将 Tee-Object 输出到终端屏幕?
不要让它写入输出流,输出流应该只用于从函数“returned”的实际数据,而不用于日志消息。
一个简单的方法是将 Tee-Object
的输出重定向到 Write-Host
,它写入信息流:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Host
更明智的方法是重定向到详细流:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Verbose
现在默认情况下,日志消息不会使终端混乱。相反,要查看详细的日志记录,调用者必须启用详细输出,例如。 G。通过设置 $VerbosePreference = 'Continue'
或使用 -Verbose
参数调用函数:
if( Is-Installed 'foo' -Verbose ){<# do something #>}