【问题标题】:Logging to console and file with function passing back return code使用函数传回返回码记录到控制台和文件
【发布时间】:2018-12-28 01:35:47
【问题描述】:

我认为由 Powershell 脚本调用的函数应该是有意义的

  1. 将相同的输出记录到日志文件和控制台中,然后
  2. 应返回指示成功/失败的状态。

我找到了一种方法来做到这一点,但它看起来非常繁琐和倒退(如下图所示)。我认为这是任何脚本语言的基本和必不可少的能力,我必须真的迷失和困惑以这种倒退的方式做某事。我对 PowerShell 很陌生,但来自 C# 背景。

我最终将-PassThru 添加到函数中的每个Add-Content 语句中,因此日志条目将作为Object[] 集合的项目返回到管道中。然后,我将返回 Object[] 集合中的最后一个布尔项,它是函数的状态。

# Main script c:\temp\test1.ps1
Function Write-FunctionOutputToConsole {
    Param ([Object[]] $FunctionResults)

    foreach ($item in $FunctionResults) {
        if ($item -is [System.Array]) {
            Write-Host $($item)
        }
    }
}

Function Get-FunctionReturnCode {
    Param ([Object[]] $FunctionResults)

    if ($FunctionResults[-1] -is [System.Boolean]) {
        Return $FunctionResults[-1]
    }
}

. c:\temp\test2.ps1  #pull in external function

$LogFile = "c:\temp\test.log"

$results = FunctionThatDoesStuff -LogFile $LogFile -DesiredReturnValue $true

Write-FunctionOutputToConsole -FunctionResults $results
$FunctionReturnCode = Get-FunctionReturnCode -FunctionResults $results
Add-Content -Path $LogFile -Value "$(Get-Date -Format G) Logging in Main: returnValue=$FunctionReturnCode" -PassThru

# Do some logic based on $FunctionReturnCode

外部函数

# c:\temp\test2.ps1
function FunctionThatDoesStuff {
    Param(
        [string] $LogFile,
        [bool]   $DesiredReturnValue
    )

    Add-Content -Path $LogFile -Value "-----------------------------------------" -PassThru
    Add-Content -Path $LogFile -Value "$(Get-Date -Format G) returnValue=$DesiredReturnValue" -PassThru
    Add-Content -Path $LogFile -Value "$(Get-Date -Format G) line 1 being logged"      -PassThru
    Add-Content -Path $LogFile -Value "$(Get-Date -Format G) line 2 being logged"      -PassThru
    return $DesiredReturnValue 
}

控制台输出:

PS C:\Temp> c:\temp\test1.ps1 ----------------------------------------- 2018 年 7 月 19 日下午 3:26:28 returnValue=True 2018 年 7 月 19 日下午 3:26:28 正在记录第 1 行 2018 年 7 月 19 日下午 3:26:28 正在记录第 2 行 2018 年 7 月 19 日下午 3:26:28 登录 Main:returnValue=True

日志文件

PS C:\Temp> 获取内容 c:\temp\test.log ----------------------------------------- 2018 年 7 月 19 日下午 3:29:59 returnValue=True 2018 年 7 月 19 日下午 3:29:59 正在记录第 1 行 2018 年 7 月 19 日下午 3:29:59 正在记录第 2 行 2018 年 7 月 19 日下午 3:29:59 登录 Main:returnValue=True

如您所见,这会导致控制台和日志文件中的信息相同。

【问题讨论】:

    标签: function powershell logging return-value


    【解决方案1】:

    我认为您误解了 PowerShell 的工作原理。一方面,最后一条命令是否成功的信息会自动存储在automatic variable$?中。如果出现错误,cmdlet 将引发可以为error handling (see also) 捕获的异常。无需使用返回值来指示成功或错误状态。此外,默认情况下,PowerShell 从函数返回 all 未捕获的输出。 return 关键字仅用于控制流。

    我会实现一个有点像这样的日志记录功能:

    function Write-LogOutput {
        [CmdletBinding()]
        Param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            [string[]]$Message,
    
            [Parameter(Position=1, Mandatory=$false)]
            [ValidateScript({Test-Path -LiteralPath $_ -IsValid})]
            [string]$LogFile = '.\default.log',
    
            [Parameter(Mandatory=$false)]
            [switch]$Quiet,
    
            [Parameter(Mandatory=$false)]
            [switch]$PassThru
        )
    
        Process {
            $Message | ForEach-Object {
                $msg = "[{0:yyyy-MM-dd HH:mm:ss}]`t{1}" -f (Get-Date), $_
                if (-not $Quiet.IsPresent) {
                    $msg | Out-Host
                }
                $msg
            } | Add-Content $LogFile
            if ($PassThru.IsPresent) {
                $Message
            }
        }
    }
    

    然后像这样使用它:

    function FunctionThatDoesStuff {
        # ...
    
        # something that should be logged, but not returned
        'foo' | Write-LogOutput -LogFile 'C:\path\to\your.log'
        # something that should be logged and returned by the function
        'bar' | Write-LogOutput -LogFile 'C:\path\to\your.log' -PassThru
        # something that should be returned, but not logged
        'baz'
    
        # ...
    }
    
    $result = FunctionThatDoesStuff
    # Output:
    # -------
    # [2018-07-19 23:44:07]   foo
    # [2018-07-19 23:44:07]   bar
    
    $result
    # Output:
    # -------
    # bar
    # baz
    

    【讨论】:

    • 哇。这里有很多高级的东西!看起来Out-Host 吃掉了管道,在向控制台/主机/屏幕显示时停止了它?是否需要使用 [CmdletBinding()] 还是更多的最佳实践? ForEach-Object 是一个接受脚本块的命令吗?看起来这是Process 内的ProcessProcess { $Message | ForEach-Object -Process { ...... } }?很好的答案。学到了很多!
    • 关于Out-HostForEach-Object:基本上是的。请阅读文档以获取更多详细信息。关于[CmdletBinding()]:请参阅hereherehere
    • 有关高级功能see here 中的BeginProcessEnd 块的信息。
    猜你喜欢
    • 2019-02-09
    • 2016-04-16
    • 1970-01-01
    • 1970-01-01
    • 2014-08-01
    • 1970-01-01
    • 2023-01-08
    • 1970-01-01
    • 2019-07-18
    相关资源
    最近更新 更多