5

I'm getting itermittent data loss when calling .NET [Console]::ReadLine() to read piped input to PowerShell.exe. In CMD, run:

>ping localhost | powershell -NonInteractive -NoProfile -C "do {$line = [Console]::ReadLine(); ('' + (Get-Date -f 'HH:mm
:ss') + $line) | Write-Host; } while ($line -ne $null)"
23:56:45time<1ms
23:56:45
23:56:46time<1ms
23:56:46
23:56:47time<1ms
23:56:47
23:56:47

Normally 'ping localhost' from Vista64 looks like this, so there is a lot of data missing from the output above:


Pinging WORLNTEC02.bnysecurities.corp.local [::1] from ::1 with 32 bytes of data:
Reply from ::1: time<1ms 
Reply from ::1: time<1ms 
Reply from ::1: time<1ms 
Reply from ::1: time<1ms 

Ping statistics for ::1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

But using the same API from C# receives all the data sent to the process (excluding some newline differences). Code:

namespace ConOutTime {
    class Program {
        static void Main (string[] args) {
            string s;
            while ((s = Console.ReadLine ()) != null) {
                if (s.Length > 0) // don't write time for empty lines
                    Console.WriteLine("{0:HH:mm:ss} {1}", DateTime.Now, s);
            } 
        }
    }
}

Output:

00:44:30 Pinging WORLNTEC02.bnysecurities.corp.local [::1] from ::1 with 32 bytes of data:
00:44:30 Reply from ::1: time<1ms
00:44:31 Reply from ::1: time<1ms
00:44:32 Reply from ::1: time<1ms
00:44:33 Reply from ::1: time<1ms
00:44:33 Ping statistics for ::1:
00:44:33     Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
00:44:33 Approximate round trip times in milli-seconds:
00:44:33     Minimum = 0ms, Maximum = 0ms, Average = 0ms

So, if calling the same API from PowerShell instead of C# many parts of StdIn get 'eaten'. Is the PowerShell host reading string from StdIn even though I didn't use 'PowerShell.exe -Command -'?

mklement0
  • 382,024
  • 64
  • 607
  • 775
yzorg
  • 4,224
  • 3
  • 39
  • 57
  • I got an error: "An expression was expected after '('." if I run this under PowerShell. Fixing your question to specify CMD. Your questions is full of good detail (thanks!) but couldn't you ahve simplified things a little by removing the `Get-Date` bit? – Jay Bazuzi May 25 '10 at 04:53
  • I confirm that the results are intermittent / unreliable. It seems to be speed-based. If `ping` is replaced with something faster (`type foo.txt`), I get no output. If I use something slow (`copy con`) I get everything. – Jay Bazuzi May 25 '10 at 05:03
  • @JayBazuzi The timestamp-per-line was the only reason I was piping through powershell in the first place. ;) – yzorg Apr 16 '13 at 21:01

3 Answers3

7

You can use the $input enumerator in PowerShell to get access to data piped into the program. I also have found that [Console]::ReadLine() somehow does little to nothing. Unknown reasons for that, though.

C:\Users\Me> ping localhost | powershell -noninteractive -noprofile -c "$input|%{(date -f HH:mm:ss)+' '+$_}"

07:31:54
07:31:54 Pinging Sigmund [::1] with 32 bytes of data:
07:31:54 Reply from ::1: time<1ms
07:31:54 Reply from ::1: time<1ms
07:31:54 Reply from ::1: time<1ms
07:31:55 Reply from ::1: time<1ms
07:31:55
07:31:55 Ping statistics for ::1:
07:31:55     Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
07:31:55 Approximate round trip times in milli-seconds:
07:31:55     Minimum = 0ms, Maximum = 0ms, Average = 0ms

C:\Users\Me>ping localhost

Pinging Sigmund [::1] with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
Joey
  • 344,408
  • 85
  • 689
  • 683
  • good trick with $input. Didn't know it is possible to use it in this way when "pipeline is outside powershell process". – stej May 25 '10 at 07:37
  • @stej: I know no other reliable way. And it's horrible for code-golfing since you can only get an array of input lines with `@($input)` but can't read line-wise when needed :-( – Joey May 25 '10 at 09:28
  • @Joey this is not working with hex conversion, example `'{0:x}' -f $input`. Can you update? – Zombo Aug 24 '14 at 07:43
  • 2
    @Steven: `$input` is an iterator, not a number. You can use various variants of `$input|%{'{0:x}'-f$_}`, `'{0:x}'-f+"$input"`, `'{0:x}'-f@($input)[0]`, ... depending on what you are trying to achieve. – Joey Aug 24 '14 at 09:13
1

One possible solution is this one:

powershell -NonInteractive -NoProfile -C  "ping localhost | % { ('' + (Get-Date -f 'HH:mm:ss') + $_) | Write-Host; }"

I'm adding it for other question visitors, cause I think you had some reasons why you don't use that :)

stej
  • 28,745
  • 11
  • 71
  • 104
0

I'm submitting my final solution, even if it wasn't really an answer to my initial question. I was trying to pipe data to powershell from a cmd.exe environment that had a bunch of specialized tools (see www.streambase.com). Eventually I added the tools into my powershell environment.

I ended up calling the external tool from within PowerShell and piping to Out-Time advanced function...

function Out-Time { 
param (
    [parameter(ValueFromPipeline=$true)]
    $Value, 
    [switch] $OutputEmptyLines=$false) 
process {
    if (!$OutputEmptyLines -and ($Value -eq $null -or $Value -eq '')) {
    } 
    else {
        "{0} {1}" -f @((get-date -Format "HH:mm:ss.ff"), $Value)
    }
} 
}
#test
#ping localhost | Out-Time; ping localhost | Out-Time -OutputEmpty
yzorg
  • 4,224
  • 3
  • 39
  • 57