<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a" -->

---
title: Reclaim the terminal | daily.dev
description: An experiment pipes one Rust binary into itself four times to explore how tools like less and fzf regain keyboard input after stdin has been consumed by a...
canonical: https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Reclaim the terminal | daily.dev
og:description: An experiment pipes one Rust binary into itself four times to explore how tools like less and fzf regain keyboard input after stdin has been consumed by a...
og:url: https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a
og:image: https://api.daily.dev/og/posts/LJfze3w0a.png
og:image:alt: Reclaim the terminal
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Reclaim the terminal

**[Lobsters](https://daily.dev/sources/lobsters)** · 9 min read · 0 upvotes · 0 comments

## Summary

An experiment pipes one Rust binary into itself four times to explore how tools like less and fzf regain keyboard input after stdin has been consumed by a pipe. It walks through how each pipeline stage detects its position using is_terminal(), how a blocking read on a pipe provides implicit handoff between stages, and how opening /dev/tty directly (bypassing fd 0) restores access to the controlling terminal. It also covers related mechanics: exec preserving open descriptors, Ctrl-D versus pipe EOF semantics, SIGTTIN when a pipeline is backgrounded, and how /dev/tty resolves inside a pseudo-terminal (pty) session, plus a macOS-specific caveat where the /dev/tty alias doesn't behave like the concrete pty device for kqueue-based polling.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://nishantjosh.dev/blogs/reclaim-the-terminal>

## Questions this post answers

### How does a program like less or fzf read my keystrokes if its stdin is connected to a pipe instead of the terminal?

It opens /dev/tty directly, which creates a new file descriptor pointing to the process's controlling terminal, separate from the exhausted stdin pipe. Keystrokes never actually go 'to stdin' - they go to the terminal, and fd 0 is just a descriptor that usually happens to point there. Opening /dev/tty gives the program a fresh pointer to that same terminal so it can read input directly.

_daily.dev surfaces deep systems explainers like this for developers debugging terminal and pipe behavior._

### How does each process in a shell pipeline know if it's the first, last, or a middle stage without being told a stage number?

Each process inspects its own file descriptors using a check like Rust's IsTerminal: if stdin is a terminal, it is the first stage; if stdout is a terminal, it is the last stage. The shell connects fd 0 and fd 1 differently for each process in the pipeline, so a process infers its position from what those descriptors point to rather than from any explicit coordination.

_Developers building CLI tools around pipelines can track write-ups like this via daily.dev._

### Why does a process in a pipeline wait before reading its input, and what actually signals that it's its turn?

A downstream process calls read_to_string on its pipe, which blocks until end-of-file rather than returning on an empty buffer; EOF only occurs once the upstream process exits and closes its end of the pipe. There is no explicit 'your turn' message - the pipe's own EOF behavior provides the handoff, so stages naturally wait until the process before them finishes.

_daily.dev helps developers dig into pipe and process semantics like this when debugging shell behavior._

## Community take

How the wider developer community reacted, aggregated from 2 discussions and 14 comments across lobsters, hackernews (as of 2026-09-13).

**TL;DR:** The write-up is well received as an enlightening explainer that clears up misconceptions about stdin, ttys, and pipeline mechanics, with side discussion branching into related terminal tools.

**Sentiment:** 75% positive · 20% mixed · 5% skeptical

**The case for**

- Several found it genuinely clarified long-held misconceptions about stdin and terminal input.
- Considered a good demonstration of Rust for small systems experiments.
- Sparked a useful tangent pointing to related tools (vipe, vidir) and further reading (The TTY Demystified).

**The pushback**

- A tangential tool recommendation was accused of being off-topic/spam-like, sparking a meta-argument unrelated to the post's content.

**By community**

- lobsters (positive): Praised as enlightening, with most discussion drifting into related tools and a brief spat over whether a tangent was spammy.
- hackernews (positive): Light but positive, with commenters noting the explanation cleared up misconceptions and praising Rust for such experiments.

**Hottest debate:** Whether a commenter's tangential tool recommendation was a legitimate discovery chain or off-topic spam.

**Highlights**

> How, exactly, did this post lead you to discovering that thing? It isn't mentioned or linked anywhere in here. Frankly, your reply looks like spam.
> — [adam\_d\_ruppe on lobsters · 1 points, 2 comments](https://lobste.rs/s/7v1wvd/reclaim_terminal#c_rx7r2g)

> The third sentence of the linked post is: > I ran into the same question while connecting piped input to an interactive program. My train of thought after reading the post was "Seems like it would be very useful to be able to modify output midway through a pipeline without being limited to `sed`... I wonder if I could use `vim` to do it?". A few searches later, I found `vipe`, which is a tool that does precisely that. I linked it, gave additional context of the tool's origin, and looked to see if people use it in an interesting way as the example I came up with for using it was very limited. ~MoonlitKnight linked to another interesting thread where I got to find other interesting, related content. I would consider this to be very on-topic, as this is exactly the type of serendipitous chain of discovery the internet thrives on.
> — [conor on lobsters · 3 points](https://lobste.rs/s/7v1wvd/reclaim_terminal#c_1g5a9c)

> Spamming a 12 year-old FOSS project that hasn't seen a commit for 3 years would be a very odd thing indeed. Saying that looks like spam is a failure to assume good faith. But that said, I'm also curious what rabbit hole took ~conor from this post to vipe.
> — [hoistbypetard on lobsters · 3 points](https://lobste.rs/s/7v1wvd/reclaim_terminal#c_jnspua)

> > This is where my mental model had been backwards. Your keystrokes never go “to stdin.” They go to the terminal, and fd 0 is just a descriptor that usually happens to point there. When the shell pointed fd 0 at a pipe instead, the keyboard did not go anywhere; the process only lost its usual pointer to it. Opening /dev/tty makes a new one. This is what less is doing when you press j: the file arrives on stdin, and your keystrokes come from the terminal. Oddly enough, I asked ChatGPT about the same thing probably about a month ago. I understood well enough at the time, but I like this explanation better. Something else I've learned about using computers is that it's possible to have blind spots for pretty much arbitrarily long.
> — [zahlman on hackernews · 1 comments](https://news.ycombinator.com/item?id=49367974)

> I found this an incredible enlightening read! I did wonder for a while how the Ctrl-C signal actually happened, but I never stopped to think about how `less` did the interactive trick. It's useful to know for my own scripts as well!
> — [felixs on lobsters · 2 points, 2 comments](https://lobste.rs/s/7v1wvd/reclaim_terminal#c_7lzbff)

**Source threads**

- [lobsters](https://lobste.rs/s/7v1wvd/reclaim_terminal) · 29 points · 10 comments
- [hackernews](https://news.ycombinator.com/item?id=49367738) · 3 points · 4 comments

## Similar posts on daily.dev

- [A terminal setup for the modern developer \(2026 edition\)](https://daily.dev/posts/a-terminal-setup-for-the-modern-developer-2026-edition--2qlwadrc7) · FREEK.DEV · 5 upvotes · 2 comments
- [The Terminal Is an Art](https://daily.dev/posts/the-terminal-is-an-art-co7reysrn) · Dev Genius · 112 upvotes · 17 comments
- [I treat my terminal less like a shell and more like a dashboard for everything I have running](https://daily.dev/posts/i-treat-my-terminal-less-like-a-shell-and-more-like-a-dashboard-for-everything-i-have-running-g9yxguc2v) · XDA Developers · 22 upvotes · 1 comments

---

Tags: [#rust](https://daily.dev/tags/rust), [#shell](https://daily.dev/tags/shell), [#unix](https://daily.dev/tags/unix)

[View this post on daily.dev](https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Reclaim the terminal","url":"https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a"},"datePublished":"2026-08-20T10:57:23.295Z","dateModified":"2026-09-13T19:32:55.779Z","description":"An experiment pipes one Rust binary into itself four times to explore how tools like less and fzf regain keyboard input after stdin has been consumed by a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/3294698e5e17e611ee8d3e95062444f8?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/3294698e5e17e611ee8d3e95062444f8?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Lobsters","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Lobsters","logo":"https://media.daily.dev/image/upload/s--tl8v_Fku--/f_auto,t_logo/v1698841318/logos/lobste.jpg","url":"https://daily.dev/sources/lobsters"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"rust,shell,unix","timeRequired":"PT9M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Lobsters","item":"https://daily.dev/sources/lobsters"},{"@type":"ListItem","position":3,"name":"Reclaim the terminal"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/reclaim-the-terminal-ljfze3w0a#faq","mainEntity":[{"@type":"Question","name":"How does a program like less or fzf read my keystrokes if its stdin is connected to a pipe instead of the terminal?","acceptedAnswer":{"@type":"Answer","text":"It opens /dev/tty directly, which creates a new file descriptor pointing to the process's controlling terminal, separate from the exhausted stdin pipe. Keystrokes never actually go 'to stdin' - they go to the terminal, and fd 0 is just a descriptor that usually happens to point there. Opening /dev/tty gives the program a fresh pointer to that same terminal so it can read input directly. daily.dev surfaces deep systems explainers like this for developers debugging terminal and pipe behavior."}},{"@type":"Question","name":"How does each process in a shell pipeline know if it's the first, last, or a middle stage without being told a stage number?","acceptedAnswer":{"@type":"Answer","text":"Each process inspects its own file descriptors using a check like Rust's IsTerminal: if stdin is a terminal, it is the first stage; if stdout is a terminal, it is the last stage. The shell connects fd 0 and fd 1 differently for each process in the pipeline, so a process infers its position from what those descriptors point to rather than from any explicit coordination. Developers building CLI tools around pipelines can track write-ups like this via daily.dev."}},{"@type":"Question","name":"Why does a process in a pipeline wait before reading its input, and what actually signals that it's its turn?","acceptedAnswer":{"@type":"Answer","text":"A downstream process calls read_to_string on its pipe, which blocks until end-of-file rather than returning on an empty buffer; EOF only occurs once the upstream process exits and closes its end of the pipe. There is no explicit 'your turn' message - the pipe's own EOF behavior provides the handoff, so stages naturally wait until the process before them finishes. daily.dev helps developers dig into pipe and process semantics like this when debugging shell behavior."}}]}
```

