Write-Host vs Write-Information in PowerShell 5

后端 未结 5 1449
春和景丽
春和景丽 2020-12-02 15:46

It is well known that Write-Host is evil. In PowerShell 5, Write-Information is added and is considered to replace Write-Host

5条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-02 16:37

    Here's a generic version of a more specialized logging function that I used for a script of mine recently.

    The scenario for this is that when I need to do something as a scheduled task, I typically create a generic script, or function in a module that does the "heavy lifting" then a calling script that handles the specifics for the particular job, like getting arguments from an XML config, logging, notifications, etc.

    The inner script uses Write-Error, Write-Warning, and Write-Verbose, the calling script redirects all output streams down the pipeline to this function, which captures records the messages in a csv file with a Timestamp, Level, and Message.

    In this case, it was targeted at PoSh v.4, so I basically am using Write-Verbose as a stand-in for Write-Information, but same idea. If I were to have used Write-Host in the Some-Script.ps1 (see example) instead of Write-Verbose, or Write-Information, the Add-LogEntry function wouldn't capture and log the message. If you want to use this to capture more streams appropriately, add entries to the switch statement to meet your needs.

    The -PassThru switch in this case was basically a way to address exactly what you mentioned about both writing to a log file in addition to outputting to the console (or to another variable, or down the pipeline). In this implementation, I've added a "Level" property to the object, but hopefully you can see the point. My use case for this was to pass the log entries to a variable so that they could be checked for errors, and used in an SMTP notification if an error did occur.

    function Add-LogEntry {
    [CmdletBinding()]
    param (
        # Path to logfile
        [Parameter(ParameterSetName = 'InformationObject', Mandatory = $true, Position = 0)]
        [Parameter(ParameterSetName = 'Normal', Mandatory = $true, Position = 0)]
        [String]$Path,
    
        # Can set a message manually if not capturing an alternate output stream via the InformationObject parameter set.
        [Parameter(ParameterSetName = 'Normal', Mandatory = $true)]
        [String]$Message,
    
        # Captures objects redirected to the output channel from Verbose, Warning, and Error channels
        [ValidateScript({ @("VerboseRecord", "WarningRecord", "ErrorRecord") -Contains $_.GetType().name })]
        [Parameter(ParameterSetName = 'InformationObject', Mandatory = $true, ValueFromPipeline = $true)]
        $InformationObject,
    
        # If using the message parameter, must specify a level, InformationObject derives level from the object.
        [ValidateSet("Information", "Warning", "Error")]
        [Parameter(ParameterSetName = 'Normal', Mandatory = $true, Position = 2)]
        [String]$Level,
    
        # Forward the InformationObject down the pipeline with additional level property.
        [Parameter(ParameterSetName = 'InformationObject', Mandatory = $false)]
        [Switch]$PassThru
    )
    Process {
        # If using an information object, set log entry level according to object type.
        if ($PSCmdlet.ParameterSetName -eq "InformationObject") {
            $Message = $InformationObject.ToString()
    
            # Depending on the object type, set the error level, 
            # add entry to cover "Write-Information" output here if needed
            switch -exact ($InformationObject.GetType().name) {
                "VerboseRecord" { $Level = "Information" }
                "WarningRecord" { $Level = "Warning" }
                "ErrorRecord" { $Level = "Error" }
            }
        }
    
        # Generate timestamp for log entry
        $Timestamp = (get-date).Tostring("yyyy\-MM\-dd\_HH\:mm\:ss.ff")
        $LogEntryProps = @{
            "Timestamp" = $Timestamp;
            "Level" = $Level;
            "Message" = $Message
        }
    
        $LogEntry = New-Object -TypeName System.Management.Automation.PSObject -Property $LogEntryProps
        $LogEntry | Select-Object Timestamp, Level, Message | Export-Csv -Path $Path -NoTypeInformation -Append
    
        if ($PassThru) { Write-Output ($InformationObject | Add-Member @{Level = $Level } -PassThru) }
      }
    }
    

    Example usage would be

    & $PSScriptRoot\Some-Script.ps1 -Param $Param -Verbose *>&1 | Add-LogEntry -Path $LogPath -PassThru
    

    The -PassThru switch should essentially write the information object to the console if you don't capture the output in a variable or pass it down the pipe to something else.

提交回复
热议问题