• When piping output to head I get 'error writing "stdout": broken pipe'

    From Cecil Westerhof@21:1/5 to All on Mon Feb 21 13:08:49 2022
    I have a tcl script that I pipe through head beacause I am only
    interested in the start lines. But this gives:
    error writing "stdout": broken pipe

    So clearly tclsh does not like it when not all output is consumed.
    Can I doe something about it, or should I use try?

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Cecil Westerhof on Mon Feb 21 14:16:40 2022
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    I have a tcl script that I pipe through head beacause I am only
    interested in the start lines. But this gives:
    error writing "stdout": broken pipe

    So clearly tclsh does not like it when not all output is consumed.

    No, the C stdlib does not like it when a pipe closes before all output
    is consumed. TCL's just the messenger here.

    Can I doe something about it, or should I use try?

    Pipe less data? :)

    Otherwise, since head is expected to exit and close the pipe, just
    catch the error and ignore it (possibly checking that you got the
    expected error and not some other, such as "executable does not
    exist").

    But you have also not explained why you are piping to head (here, yet
    you complained about exec'ing grep for your proc file parser, both
    result in the same fork/exec cost).

    You can get the first five lines in Tcl, assuming the whole string is
    already loaded, like so:

    set first_file [lrange [split $multi_line_string \n] 0 4]

    This does produce a list. If you want them packed into a multi-line
    string again, then add a join.

    set first_five [join [lrange [split $multi_line_string \n] 0 4] \n]

    You'll have to use [time] to compare performance, but depending upon
    the size of $multi_line_string, the alloc's and free's for the pure Tcl
    version may likely be faster than a fork/exec cycle. And if you redo
    the "head" several times then with the Tcl version you can hold onto
    the output from split in a separate variable to factor out the split
    into a single call for all subsequent "head" operations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Rich on Mon Feb 21 16:35:23 2022
    Rich <rich@example.invalid> writes:

    Cecil Westerhof <Cecil@decebal.nl> wrote:
    I have a tcl script that I pipe through head beacause I am only
    interested in the start lines. But this gives:
    error writing "stdout": broken pipe

    So clearly tclsh does not like it when not all output is consumed.

    No, the C stdlib does not like it when a pipe closes before all output
    is consumed. TCL's just the messenger here.

    Can I doe something about it, or should I use try?

    Pipe less data? :)

    Otherwise, since head is expected to exit and close the pipe, just
    catch the error and ignore it (possibly checking that you got the
    expected error and not some other, such as "executable does not
    exist").

    But you have also not explained why you are piping to head (here, yet
    you complained about exec'ing grep for your proc file parser, both
    result in the same fork/exec cost).

    That is a very standard thing to do in Unix/Linux: when you are only
    interested in the first part of output. Never had any problem with
    that.

    I solved it with:
    foreach swapElement [lrange ${swapList} 0 $end] {
    set name [lindex ${swapElement} 0]
    set swap [expr {[lindex ${swapElement} 1] / 1024.}]
    try {
    puts [format "%-15s: %.2E" $name $swap]
    } on error msg {
    if {${::errorCode} == {POSIX EPIPE {broken pipe}}} {
    break
    }
    fatalError $msg
    }
    }

    I can now give a value for the number of lines, but when someone uses
    it in a pipe it should not create an error.

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Uwe Klein@21:1/5 to All on Mon Feb 21 17:10:46 2022
    Am 21.02.22 um 16:35 schrieb Cecil Westerhof:
    That is a very standard thing to do in Unix/Linux: when you are only interested in the first part of output. Never had any problem with
    that.

    Are you handling a file or piping from a tcl interpreter?

    % exec head --lines 1 dummy.txt
    ###########################################
    %

    helps: ?? -ignorestderr ??

    % set content {============================================================================= slkdfasdlkfasdlkfsaldkf
    asd
    fas
    fsad
    fsad
    fsd
    f
    }
    .....
    % exec head --lines 1 <<$content ==========================================================================
    %

    i.e. I can't reproduce your hickup.

    Uwe

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Mon Feb 21 18:46:23 2022
    * Cecil Westerhof <Cecil@decebal.nl>
    | I can now give a value for the number of lines, but when someone uses
    | it in a pipe it should not create an error.

    Then you absolutely need to 'catch' the puts and handle errors according
    to ::errorCode as Rich suggested upthread.

    % set fd [open "|echo foo" w]
    % fconfigure $fd -buffering none
    % catch {puts $fd foo}
    1
    % set errorCode
    POSIX EPIPE {broken pipe}

    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Ralf Fassel on Tue Feb 22 08:51:03 2022
    Ralf Fassel <ralfixx@gmx.de> writes:

    * Cecil Westerhof <Cecil@decebal.nl>
    | I can now give a value for the number of lines, but when someone uses
    | it in a pipe it should not create an error.

    Then you absolutely need to 'catch' the puts and handle errors according
    to ::errorCode as Rich suggested upthread.

    % set fd [open "|echo foo" w]
    % fconfigure $fd -buffering none
    % catch {puts $fd foo}
    1
    % set errorCode
    POSIX EPIPE {broken pipe}

    That is what I showed I was now doing:
    } on error msg {
    if {${::errorCode} == {POSIX EPIPE {broken pipe}}} {
    break
    }
    fatalError $msg
    }

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Uwe Klein on Tue Feb 22 09:08:17 2022
    Uwe Klein <uwe@klein-habertwedt.de> writes:

    Am 21.02.22 um 16:35 schrieb Cecil Westerhof:
    That is a very standard thing to do in Unix/Linux: when you are only
    interested in the first part of output. Never had any problem with
    that.

    Are you handling a file or piping from a tcl interpreter?

    % exec head --lines 1 dummy.txt
    ###########################################
    %

    helps: ?? -ignorestderr ??

    % set content {=============================================================================
    slkdfasdlkfasdlkfsaldkf
    asd
    fas
    fsad
    fsad
    fsd
    f
    }
    .....
    % exec head --lines 1 <<$content ==========================================================================
    %

    i.e. I can't reproduce your hickup.

    I was a bit flabbergasted, because at first I could not reproduce it.
    But there has to be enough buffered to get the error.
    For example when you have this code in dummy.tcl:
    #!/usr/bin/env tclsh

    for {set x 0} {$x < 20} {incr x} {
    puts "Just some output"
    }

    and use:
    dummy.tcl | head -n 5

    everything seems fine.
    But when you change the 20 to 21 then sometimes it works and sometimes
    it does not.
    Up to 35 it is the same.
    When making it 50 most of the times (but still not always) you get the
    error.

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Cecil Westerhof on Tue Feb 22 09:18:48 2022
    Cecil Westerhof <Cecil@decebal.nl> writes:

    Uwe Klein <uwe@klein-habertwedt.de> writes:

    Am 21.02.22 um 16:35 schrieb Cecil Westerhof:
    That is a very standard thing to do in Unix/Linux: when you are only
    interested in the first part of output. Never had any problem with
    that.

    Are you handling a file or piping from a tcl interpreter?

    % exec head --lines 1 dummy.txt
    ###########################################
    %

    helps: ?? -ignorestderr ??

    % set content
    {=============================================================================
    slkdfasdlkfasdlkfsaldkf
    asd
    fas
    fsad
    fsad
    fsd
    f
    }
    .....
    % exec head --lines 1 <<$content
    ========================================================================== >> %

    i.e. I can't reproduce your hickup.

    I was a bit flabbergasted, because at first I could not reproduce it.
    But there has to be enough buffered to get the error.
    For example when you have this code in dummy.tcl:
    #!/usr/bin/env tclsh

    for {set x 0} {$x < 20} {incr x} {
    puts "Just some output"
    }

    and use:
    dummy.tcl | head -n 5

    everything seems fine.
    But when you change the 20 to 21 then sometimes it works and sometimes
    it does not.
    Up to 35 it is the same.
    When making it 50 most of the times (but still not always) you get the
    error.

    This one does not go wrong, but only signals that an broken pipe was
    generated:
    #!/usr/bin/env tclsh


    for {set x 0} {$x < 1000} {incr x} {
    try {
    puts "Just some output"
    } on error msg {
    if {${::errorCode} == {POSIX EPIPE {broken pipe}}} {
    puts stderr "Got a broken pipe."
    break
    }
    puts stderr "Got an unexpected '${::errorCode}'"
    exit 1
    }
    }

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Cecil Westerhof on Tue Feb 22 13:18:59 2022
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    Uwe Klein <uwe@klein-habertwedt.de> writes:

    Am 21.02.22 um 16:35 schrieb Cecil Westerhof:
    That is a very standard thing to do in Unix/Linux: when you are
    only interested in the first part of output. Never had any problem
    with that.

    Are you handling a file or piping from a tcl interpreter?

    % exec head --lines 1 dummy.txt
    ###########################################
    %

    helps: ?? -ignorestderr ??


    i.e. I can't reproduce your hickup.

    I was a bit flabbergasted, because at first I could not reproduce it.
    But there has to be enough buffered to get the error.

    Yes, exactly.

    The error is from libc, and indicates that the C library I/O system had
    output data buffered, that had not been consumed by the forked program,
    when the forked program closed its input file.

    Which is why you have to output a certian amount before triggering it.
    There has to be buffered data in libc's stdio buffer that has not been transferred to the process on the other side of the channel, before the
    error will be returned.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andreas Leitgeb@21:1/5 to Cecil Westerhof on Wed Feb 23 10:17:48 2022
    Cecil Westerhof <Cecil@decebal.nl> wrote:
    try {
    puts "Just some output"
    } on error msg {
    if {${::errorCode} == {POSIX EPIPE {broken pipe}}} {
    puts stderr "Got a broken pipe."
    break
    }
    puts stderr "Got an unexpected '${::errorCode}'"
    exit 1
    }

    Just for your getting to know "try" better, see what its "trap"-handlers
    can do for you. :-)


    If your tool is *designed* to be used in pipelines that may
    shut off before eof, I'd suggest to create an "exit-on-any-error"
    puts-wrapper, like this:

    % proc eoaeputs {args} { if {[catch {puts {*}$args}]} { exit } }

    and use that instead of "puts" for all lines written to stdout.

    For i/o-tools like that, even a "broken pipe"-message on stderr can
    be a nuissance.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cecil Westerhof@21:1/5 to Andreas Leitgeb on Wed Feb 23 12:39:22 2022
    Andreas Leitgeb <avl@logic.at> writes:

    Cecil Westerhof <Cecil@decebal.nl> wrote:
    try {
    puts "Just some output"
    } on error msg {
    if {${::errorCode} == {POSIX EPIPE {broken pipe}}} {
    puts stderr "Got a broken pipe."
    break
    }
    puts stderr "Got an unexpected '${::errorCode}'"
    exit 1
    }

    Just for your getting to know "try" better, see what its "trap"-handlers
    can do for you. :-)

    I will look into that.


    If your tool is *designed* to be used in pipelines that may
    shut off before eof, I'd suggest to create an "exit-on-any-error" puts-wrapper, like this:

    Well, in this case you almost good say it is designed for that, but in
    linux land it is very normal to use head when you are only interested
    in the first few lines of output, so you would need it always. ;-)


    % proc eoaeputs {args} { if {[catch {puts {*}$args}]} { exit } }

    and use that instead of "puts" for all lines written to stdout.

    For i/o-tools like that, even a "broken pipe"-message on stderr can
    be a nuissance.

    The broken pipe message was only to verify that I got it. I should
    have removed it before posting. :'-(

    When I get a broken pip I do not see it as an error and just continue
    (and can do cleanup if necessary), but another error should be clearly communicated in my opinion.

    --
    Cecil Westerhof
    Senior Software Engineer
    LinkedIn: http://www.linkedin.com/in/cecilwesterhof

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)