Home > OS >  How can I make `Tee-Object` not adding a trailing newline in PowerShell?
How can I make `Tee-Object` not adding a trailing newline in PowerShell?

Time:12-15

Tee-Object does not have a -NoNewline switch like many other output-to-file-cmdlets (e. g. Out-File, Set-Content). Under the hood, Tee-Object uses Out-File to write to a file, which adds a trailing newline by default.

As I (currently) cannot pass through the -NoNewline switch through Tee-Object, is there another way I can enforce that the underlying Out-File won't add a trailing newline? Looking at the implementation of Out-File, there might be now way, but maybe someone is aware of some tricks/hacks to achieve it anyway?

Some constraints:

  • I want to use Tee-Object (or an alternative solution) in a pipeline
  • I do not want to post-process the files generated by Tee-Object (or an alternative solution), like opening them again and removing the (last) newline.

Code for reproduction:

"Test" | Tee-Object file | Out-Null

On Windows, the generated file file will contain 6 bytes like shown in the following hexdump:

          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
00000000  54 65 73 74 0D 0A                               Test..

Which, unfortunately, contains the additional bytes 0D 0A a. k. a. `r`n or CR&LF in Windows.

CodePudding user response:

You could roll your own and write a prefixed newline:

function Tee-StackProtectorObject {
    param(
        [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [psobject]
        $InputObject,

        [Parameter(ParameterSetName='Path', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)]
        [string[]]
        $Path,

        [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Alias('PSPath')]
        [string[]]
        $LiteralPath
    )

    begin {
        # Determine newline character sequence at the start (might differ across platforms)
        $newLine = [Environment]::NewLine

        # Prepare parameter arguments for Add-Content
        $addContentParams = @{ NoNewLine = $true }
        if($PSCmdlet.ParameterSetName -eq 'Path'){
            $addContentParams['Path'] = $Path
        }
        else {
            $addContentParams['LiteralPath'] = $LiteralPath
        }
    }

    process {
        # Write to file twice - first a newline, then the content without trailling newline
        Add-Content -Value $newLine @addContentParams
        Add-Content -Value $InputObject @addContentParams

        # Write back to pipeline
        Write-Output -InputObject $InputObject
    }
}

Note that, unlike Tee-Object, the function above is in perpetual "append mode". Refactoring it to support both appending and overwriting is left as an exercise for the reader :)

  • Related