Powershell: PSCore: Linux - Clear-Host injects data into function return value

Created on 19 Jul 2019  路  11Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

Function Get-SomeData {
    Clear-Host
    $somedata="somedata"
    return $somedata
}

$myData = Get-SomeData

$myData[0]     # will clear the screen
$myData[1]     # "somedata"
$myData.Count  # will be 2

Expected behavior

Function should return a single value ("somedata")

Actual behavior

Function returns an array with the Clear-Host command as the first object

Environment data

Verified in:
|Name|Value|
|---|---|
|PSVersion|6.2.0|
|PSEdition|Core|
|GitCommitId|6.2.0|
|OS|Linux 4.15.0-47-generic #50-Ubuntu SMP Wed Mar 13 10:44:52 UTC 2019|
|Platform|Unix|
|PSCompatibleVersions|1.0 2.0 3.0 4.0 5.0 5.1.10032.0 6.2.0|
|PSRemotingProtocolVersion|2.3|
|SerializationVersion|1.1.0.1|
|WSManStackVersion|3.0|

Issue-Bug Resolution-Fixed WG-Interactive-Console

Most helpful comment

@mklement0 Many thanks! I pulled PR with your suggestion.

All 11 comments

(BTW this works as expected in PSCore - Windows)

Clear-Host on Linux calls the native command /usr/bin/clear,

// Porting note: non-Windows platforms use `clear`
return @"
& (Get-Command -CommandType Application clear | Select-Object -First 1).Definition

which works by outputting a string with ANSI escape codes:

<esc>[3;J<esc>[H<esc>[2J

They are: clear screen including scrollback, move cursor to top left corner, clear screen. Source.

8554 and #8603 and #8606 and #8609 are related issues, and it looks like there's some debate about whether it's PowerShell or .Net's responsibility, what the default bahviour should be, and how it should interact with "clearing" remote sessions or newly implemented hosts.

As a Linux-specific workaround, you could write that string (or the output of clear) to the host yourself:

Function Get-SomeData {
    $Host.UI.Write("`e[3;J`e[H`e[2J")
    $somedata="somedata"
    return $somedata
}

$myData = Get-SomeData

Sure, I could also (as I've done) fork the logic:

if ($isLinux) { etc }

... but that kind of obviates the point of a cross-platform language, doesn't it? :)

I think what's more concerning to me is why Clear-Host is messing with my variable stack. Whatever the subtle differences (clear screen, clear buffer+screen, etc) it definitely shouldn't be _returning a value_.

But that seems to be what's happening here.

Actually the more elegant fix, in case anyone runs across this, is simply:
$null = Clear-Host

Which will clear the call stack so everything behaves as expected. But it's still code behaving badly.

When I try that fix on Ubuntu 16, the screen is not cleared?

why Clear-Host is messing with my variable stack. Whatever the subtle differences (clear screen, clear buffer+screen, etc) it definitely shouldn't be returning a value.

All command output or free values anywhere in a PowerShell function become part of the return value of the function (that decision was taken to copy sh/bash/ksh/etc. behaviour). Clear-Host particularly returns a value because it wraps /user/bin/clear a native command which works by returning a value.

clear doesn't clear the screen directly, it prints a string expecting the display terminal to clear itself. If you do something to block that, the screen doesn't clear and you get the string instead. The same thing happens in Bash; at a shell prompt:

clear                      # screen is cleared
clear 1>/tmp/test          # screen is not cleared, reset string written to file


result=`clear`             #screen is not cleared, output -> variable
echo $result               # screen is cleared.


function test { clear; echo "123"; }
test                       # screen is cleared and 123 is output
result=`test`              # screen is not cleared, no visible output
echo $result | wc -c       # result variable has 16 characters, not 3
16

echo $result               # screen is cleared, 123 is output

that kind of obviates the point of a cross-platform language, doesn't it? :)

It seems to behaves like the platform it's running on. Whether that's badly or not, depends on what behaviour you want 馃

I think it should be a bug because script behavior varies between platforms.
Clear command issues escapes and PowerShell intercepts this. Perhaps we could re-send by raw interface API.

/cc @mklement0

I agree, @iSazonov.

@HumanEquivalentUnit: True, but since PowerShell, unlike Bash, is able to distinguish between to-terminal output and success-stream output, the fix is to write directly to the terminal:

${function:Clear-Host} prints the function definition, and on Unix-like platforms you get:

& (Get-Command -CommandType Application clear | Select-Object -First 1).Definition

The above writes implicitly to the success output stream, so wrapping it in [Console]::Write() fixes the problem:

function Clear-Host {
  [Console]::Write((
    & (Get-Command -CommandType Application clear | Select-Object -First 1).Definition
  ))
}

$host.UI.Write() would work too and is arguably conceptually better, but since /usr/bin/clear is being called, the code is terminal (console)-specific anyway.

Test:

& { Clear-Host; 'hi' } | Should -Be 'hi'

(Note: In PowerShell Core 7.0.0-preview.4 I get a spurious Suggestion [4,General]: The most similar commands are: New-Object, Tee-Object. message the first time Should is run in the session, but this is unrelated and can be ignored).

@mklement0 Many thanks! I pulled PR with your suggestion.

Thanks for the quick turnaround, @iSazonov.

Taking a step back: By _unconditionally_ delegating to /usr/bin/clear on Unix-like platforms, we've actually already made Clear-Host _terminal_-specific, which means that it may malfunction / not do anything meaningful in other types of hosts.

To quote @BrucePay from one of the linked issues, at https://github.com/PowerShell/PowerShell/pull/8603#discussion_r246132597:

This isn't going to work for non-console hosts [...] and remoting. On those platforms, you still need to use the $RawUI portability APIs. A nice solution would be to actually add a new RawUI.Clear() API and then implement that using Console.Clear() in the console host but that may end up being a bunch of work...

Case in point: If you currently execute Clear-Host in a remoting session connected to a Unix machine, it prints the following message to stderr (and does nothing else).

TERM environment variable not set.

@mklement0 I see we have some issues with Clear-Host. but I still don't have the ability to test on Unix-s and can not grab this. (Although we could do RawUI.Clear() step by step).

:tada:This issue was addressed in #10681, which has now been successfully released as v7.0.0-preview.5.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings