3

I have a pipeline of (two) commands that I need to process input from terminal. With one command, this would be easy:

grep "b"

reads from stdin, which is terminal and prints lines containing b to stdout (also terminal).

But if I pipeline another command, for example:

grep "b" | tr 'a' 'a'

it still takes input from terminal, but does not ever output anything. The only way I could force it to do anything is by starting the pipeline with file like so:

cat "file.txt" | grep "b" | tr 'c' 'a'

Then it behaves like expected, printing the lines of file.txt that contain b and replacing all occurences of c with a.

So, is there any way for this to read stdin instead of "file.txt", like it does when there is only one command?

cat - | grep "b" | tr 'c' 'a'

did not work.

2 Answers 2

0

There are probably multiple ways to do this. A common way would be to use "read" to read input from a stdin. Unfortunately this only handles 1 line at a time so its normally encapsulated in a loop.

Another, more hackey solution might be to use a named pipe or socket to "inject" input from another TTY or socket.

For some kinds if guided input it might be appropriate to use "expect" in place of read.

1
  • Read in a loop worked for me. Hoped for an one-liner, though :(
    – Damir Gr
    Oct 7, 2020 at 12:07
1

grep b | tr c a does read from the terminal. You don't immediately see the output from the entire pipeline because there's a buffer between grep and tr.

Possible scenarios:

  • If you write enough matching data then the buffer will eventually fill up, tr will get some input and you will see some output. This is hard to get with purely manual input because the buffer can be relatively large
  • If you indicate EOF by hitting Ctrl+D then tr will get all it should get and you will see all the output you expect. It's easy to test this manually.
  • But if you give up instead and hit Ctrl+C then you will terminate the pipeline and lose whatever data is in the buffer. I guess this is what you did, this is why you think the command "does not ever output anything".

Your grep may or may not support the --line-buffered option. If it does then the following command will print matching lines immediately:

grep --line-buffered b | tr c a

You may have experienced the problem with something other than grep. For any program try these general solutions (where grep b is only an example):

stdbuf -o L grep b | tr c a
unbuffer -p grep b | tr c a    # probably without echoing input

Note stdbuf and unbuffer work in different ways. I prefer stdbuf.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .