# Reading lines from a pipe without hanging at the end

**URL:** <https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524>\
**Category:** General Usage\
**Tags:** pipes\
**Created:** [October 10, 2021, 9:51pm UTC](https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524 "2021-10-10T21:51:01Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [October 10, 2021, 9:51pm UTC](https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524/1 "2021-10-10T21:51:01Z")

</div>

Suppose you want to run a command, read the output, and return some processed version of the output…

for example this code parses the output of “traceroute”…

```julia

function gethops(dname)

    trin = Pipe()
    trout = Pipe()
    trerr = Pipe()
    trproc = run(pipeline(`traceroute -n -w 0.5 $dname`; stdin=trin,stdout=trout,stderr=trerr),wait=false)

    hops = DataFrame()
    @async (sleep(5);close(trout));
    readline(trout) ## throw away the first line
    while true
        if(!eof(trout))
            line = lstrip(readline(trout));
            s = split(line,r"[*]+")
            push!(hops,(n=s[1],addr=s[2],ms=s[3]))
            #@show hops
        else
            break;
        end
    end
    return hops
end

gethops("www.google.com")

```

Without the `@async (sleep(5);close(trout)` the function hangs because after the command exits the pipe is not closed. That seems weird. What’s the right way to read all the lines output by a command without hanging after the task ends?

(note, I could also do `@async(wait(trproc); close(trout))` which might make more sense than a fixed timeout… though I could also imagine a process that hangs without exiting… and we want a timeout as well? another `@async`?)

---

<div class="post-metadata">

**Author:** ![Ralph\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ralph_smith/32/10344_2.png) [@Ralph\_Smith](https://discourse.julialang.org/u/Ralph_Smith)\
**Post date:** [October 11, 2021, 2:52am UTC](https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524/2 "2021-10-11T02:52:40Z")

</div>

Would `while process_running(trproc)` work for your application?

IIUC `EOF` means that _all_ potential writers have exited or closed the descriptor, but Julia (or libuv?) seems to preserve pipes as zombies.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [October 11, 2021, 4:47am UTC](https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524/3 "2021-10-11T04:47:27Z")

</div>

This seems to make sense. The zombie pipe is a little weird though, once the process is done and all text has been read, what else could write to the pipe? Shouldn’t it be eof?

---

<div class="post-metadata">

**Author:** ![Ralph\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ralph_smith/32/10344_2.png) [@Ralph\_Smith](https://discourse.julialang.org/u/Ralph_Smith)\
**Post date:** [October 12, 2021, 4:01am UTC](https://discourse.julialang.org/t/reading-lines-from-a-pipe-without-hanging-at-the-end/69524/4 "2021-10-12T04:01:02Z")

</div>

On second thought, my suggestion is wrong because of races. Better solutions I can think of are not properly documented.

The conventional (POSIX) approach is to call

`close(Base.pipe_writer(trout))`

after the `run` command.

It turns out that libuv provides one-sided pipes, so the alternative is

`trout = Base.PipeEndpoint()`

Both of these have the effect that `eof(trout)` only depends on the subprocess, so your logic should work.

The zombie issue is that otherwise the pipe end held by Julia isn’t closed until a finalizer runs when the Pipe object gets garbage-collected.
