4

It seems read does not write to standard output.

$ cat test.sh
#!/bin/bash
read -p "type a key and Enter" key
echo "the key was $key"
$ ./test.sh
type a key and Enterx
the key was x
$ ./test.sh | tee file
type a key and Enterx
the key was x
$ cat file
the key was x

Trying it without a script...

$ read -p "type a key and Enter" key | tee file
type a key and Enterx
$ cat file
$ 

The read command is described here. A brief description: "Read a line from standard input".

Information on the pipe, |, from The Linux Command Line (2nd Internet Edition) by William E. Shotts follows.

Using the pipe operator "|" (vertical bar), the standard output of one command can be piped to into the standard input of another: command1 | command2

From the man page for tee is this description:

read from standard input and write to standard output and files

The man page for tee is at this link.

Where is the list of all the other examples of programs that write to the console but actually do not use standard output, or in the absence of such a list, what is the general rule for knowing when a program that writes to the console is not using standard output?

It seems read violates the Principle of Least Astonishment.

wjandrea
  • 14,236
  • 4
  • 48
  • 98
H2ONaCl
  • 9,693
  • @Terrance I thought that too, but what he's actually asking about is the prompt from read. I'm writing up an answer ATM. – wjandrea Jan 07 '17 at 04:07
  • @Terrance No, read outputs a prompt: type a key and Enter – wjandrea Jan 07 '17 at 04:09
  • @H2ONaCl What are you trying to accomplish? Are you trying to grab input and send it to a file as output with the tee command? – Terrance Jan 07 '17 at 04:21
  • I am trying to document part of a terminal session by piping output to a file. The prompt "type a key and Enter" is part of the session so it would be better if it were in the file. – H2ONaCl Jan 07 '17 at 05:45
  • 2
    @H2ONaCl You may want to use the command script for that instead. – wjandrea Jan 07 '17 at 05:51
  • What is your question? – David Foerster Jan 07 '17 at 12:16
  • read -p writes to the standard error stream. See https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-read. – David Foerster Jan 07 '17 at 12:19
  • I updated my answer to use echo lines instead. Turns out through testing that if you want to keep your variable, the read with prompt does not capture what you set the variable to. So, the capture of the key ends up blank. – Terrance Jan 07 '17 at 15:27

2 Answers2

6

In your example, read is writing to stderr instead of stdout. Here's how I know:

$ read -p "Type a key and press Enter: " key >/dev/null
Type a key and press Enter: x
$ read -p "Type a key and press Enter: " key 2>/dev/null
x

You can do this test on any program whose output you want to redirect.

So the command you are looking for is this:

$ read -p "Type a key and press Enter: " key 2>&1 | tee file
Type a key and press Enter: x
$ cat file
Type a key and press Enter: $

For explanations of the above redirections:

wjandrea
  • 14,236
  • 4
  • 48
  • 98
  • This solution puts the prompt in the tee file. Terrance is correct that the key itself will be missing from the file so the console and the file will differ in appearance. – H2ONaCl Jan 08 '17 at 03:41
  • The imperfect solution is to have the script explicitly echo the the key. So the script would have to have 2 lines. The first line is read -p "type a key and hit Enter " key and the second line is echo "$key". Then run it this way ./script.sh 2>&1 | tee file. The console and file will still fail to be exactly the same in appearance but at least the key appears in the file. All this is inferior to wjandrea's suggestion to use script to record a console session. – H2ONaCl Jan 08 '17 at 03:44
  • @H2ONaCl I'm not sure what you mean by these comments. The question isn't about input, just output. – wjandrea Jan 08 '17 at 04:22
1

With what wjandrea said, that is correct that you can grab the stderr that read is doing. Unfortunately, trying to read in with the stderr does not keep the variable of key, so the output for echo $key will end up blank. However, in a script it will keep the variable because the read line itself is not being redirected from stderr to stdout 2>&1 in the script. Instead, script gets to keep the variable because it is now set before it gets to redirection. I think to make it easier, use echo lines instead. I have added all examples.

Example of your script:

:~$ cat test1.sh 
#!/bin/bash
read -p "type a key and Enter: " key
echo "the key was $key"

:~$ ./test1.sh | tee junk
type a key and Enter: G
the key was G

:~$ cat junk
the key was G

With 2>&1 redirection:

:~$ ./test1.sh 2>&1 | tee junk
type a key and Enter: G
the key was G

:~$ cat junk
type a key and Enter: the key was G

With echo line:

:~$ cat test.sh 
#!/bin/bash
echo -n "Type a key and press Enter: "
read key
echo "the key was $key"

echo line with tee command:

:~$ ./test.sh | tee junk
Type a key and press Enter: X
the key was X

:~$ cat junk
Type a key and press Enter: the key was X

Example of the no script:

With stderr:

:~$ read -p "Type a key and press Enter: " key 2>&1 |tee junk; echo "The key was $key" | tee -a junk
Type a key and press Enter: F
The key was 

:~$ cat junk
Type a key and press Enter: The key was 

With echo and multiple tee commands instead:

:~$ echo -n "Type a key and press Enter: " | tee junk; read key; echo "The key was $key" | tee -a junk
Type a key and press Enter: F
The key was F

:~$ cat junk
Type a key and press Enter: The key was F

Hope this helps!

Terrance
  • 41,612
  • 7
  • 124
  • 183