1

I can redirect the stdout/stderr of everything in a script like this:

#!/bin/bash
exec > stdout_file
echo example

But now I want the stdin of the whole script to be written into a file. I tried it like this:

#!/bin/bash
exec 0>stdin_file
sleep 5

If I enter "foo" while the sleep is running it will not be written to the file but bash will try to run the command "foo" after the script exited. I assume this is because bash connects stdin to the stdin of sleep but sleep does not read from stdin.

I want all user input to be redirected to the file, rather than being sent to the stdin of the commands in the script (represented by 'sleep' here).

Among others, I also tried

cat - >stdin_file &

but this doesn't work since bash disconnected cats stdin when sending it to the background.

3
  • Maybe something like tee stdin_file | script? (Instead of script you could also have multiple commands enclosed in curly braces or parentheses. Depending on your script you might get buffering issues if the input is not a terminal but a pipe.
    – Bodo
    Commented May 24, 2023 at 12:01
  • This answer of mine provides a way for a shell script to run itself with script automatically. You got an answer that uses script, so maybe you will find the linked answer useful. Commented May 24, 2023 at 16:05
  • I suggest to edit your question and add more details. Explain your use case. What do you want to achieve at the end? Do the commands in the script normally use stdin? You wrote "I want all user input to be redirected to the file, rather than being sent to the stdin of the commands in the script". If the user input shall not be sent to the commands, what else should happen with the commands' stdin? Close the file descriptor? EOF? Endless input?
    – Bodo
    Commented May 24, 2023 at 18:10

2 Answers 2

1

I'm not sure what your goal is, but trying to get in between the terminal and an interactive command can be somewhat tricky. A tool that's designed to do this is script(1), from util-linux.

Let's say you have a bridge.sh containing:

#!/bin/sh
read -p "What… is your name? " NAME
read -p "What… is your quest? " QUEST
read -p "What… is your favourite colour? " COLOUR
echo "Go on. Off you go."

You can capture the terminal session with:

script -c ./bridge.sh bridge_script

Since you're only interested in the user's input, you can use:

script -c ./bridge.sh --log-in bridge_input_script

The manual page warns:

script places everything in the log file, including linefeeds and backspaces. This is not what the naive user expects.

And indeed, you can see in the input log that there's a bare CR at the end of each of my inputs. (Which makes sense; I hit , it logged CR.)

> cat -A bridge_input_script
Script started on 2023-05-24 09:46:19-04:00 [TERM="xterm-256color" TTY="/dev/pts/4" COLUMNS="124" LINES="40"]$
My name is Sir Lancelot of Camelot.^MTo seek the Holy Grail.^MBlue.^M$
Script done on 2023-05-24 09:46:32-04:00 [COMMAND_EXIT_CODE="0"]$

(This is really the best it can do without knowing more about the command under instrumentation.)

This is what I usually do, to make sure I have all the data I might need for future analysis:

script -c ./bridge.sh --log-timing bridge_timing_log --log-io bridge_io_log

And I use scriptreplay(1) play back the session:

scriptreplay --log-io bridge_io_log --log-timing bridge_timing_log -m 0.01

(Since I don't care about the timing much, I pass -m 0.01 to cap the maximum delay at 10ms.)

script has a few more tricks up its sleeve, including scriptlive(1), which actually re-runs your session from the logs. (You can really shoot yourself in the foot with this. RTFM and be careful!)

1
#! /bin/bash -

tee input.log | {
  # contents of your script here
}

Would save stdin to input.log as well as feeding it to the code inside the braces. That code will see the input coming from a pipe though, which may affect its behaviour if it was expecting it coming from a tty device.

You can save both input and output with:

#! /bin/bash -
set -o pipefail
tee input.log | {
  # contents of your script here
} | tee output.log

(pipefail so as not to miss failures of either tee or the code, though beware it will also affect the exit status of pipelines inside the code).

You must log in to answer this question.

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