3

I have a script which outputs things on /dev/tty, so it cannot be outputed into logs or anything by default. I would like to capture ALL outputs of the given script from another script to store it to one variable, including the ones on /dev/tty, or the ones made from read commands.

File: prompt_yes_no.sh (which I can't touch)

#! /bin/bash
source $SH_PATH_SRC/in_array.sh

function prompt_yes_no() {
  # Correct answer to provide
  correct_answers=(y Y n N)
  local answer=""
  # While the answer has not been given
  while :
  do
    # Pop the question
    read -p "$1 " answer
    # Filter the answer
    if in_array "$answer" "${correct_answers[@]}"; then
      # Expected answer
      break
    fi
    # Failure to answer as expected
    if [ $# -gt 1 ]; then
      # Set message, /dev/tty prevents from being printed in logs
      echo "${2//$3/$answer}" > /dev/tty
    else
      # Default message
      echo "'$answer' is not a correct answer." > /dev/tty
    fi
  done
  # Yes, return true/0
  if [ "$answer" == "y" ] || [ "$answer" == "Y" ]; then
    return 0
  else
    # No, return false/1
    return 1
  fi
}

File: test-prompt_yes_no.sh: (which I'm doing)

#! /bin/bash

# Helpers includes
source $SH_PATH_HELPERS/test_results.sh # contains assert()

# Sources
source $SH_PATH_SRC/prompt_yes_no.sh

ANSWER_OK="You agreed."
ANSWER_DENIED="You declined."
PATT_ANSWER="__ANSWER__"
DEFAULT_DENIED_MSG="'$PATT_ANSWER' is not a correct answer."

function prompt() {
  if prompt_yes_no "$@"; then
    echo "$ANSWER_OK"
  else
    echo "$ANSWER_DENIED"
  fi
}

function test_promptYesNo() {
  local expected="$1"
  result=`printf "$2" | prompt "${@:3}"`
  assert "$expected" "$result"
}

test_promptYesNo $'Question: do you agree [y/n]?\nYou agreed.' "y" "Question: do you agree [y/n]?"
test_promptYesNo $'Question: do you agree [y/n]?\nYou declined.' "n" "Question: do you agree [y/n]?"
test_promptYesNo $'Question: do you agree [y/n]?\n\'a\' is not a correct answer.\nYou declined.' "a\nn" "Question: do you agree [y/n]?"

The test would read all outputs redirected to /dev/tty from the first script, capture them so I could compare.

I tried to exec /dev/tty >&1 at the beginning of the second script to redirect the tty to standard output, but I get 'Permission denied' error.

Sylordis
  • 142

2 Answers2

4

You can record everything a program displays on a terminal with script. This program comes from BSD and is available on most Unix platforms, sometimes packaged with other BSD tools, and very often part of the most basic installation. Unlike redirection, which causes the program to output to a regular file, this works even if the program requires its output to be a terminal.

  • Can you provide an example to redirect output from /dev/tty using script? – Akash Karnatak Jul 07 '20 at 06:27
  • It's not really redirection. Instead, it might be better to say script captures output to /dev/tty and copies it to the script file. It still goes to /dev/tty as well. – 4dummies Jan 11 '21 at 21:39
3

Since you know the contents of prompt_yes_no.sh you can edit them before including them, replacing the /dev/tty by, for example, stdout. Replace your source by

source <(sed 's|/dev/tty|/dev/stdout|g' <$SH_PATH_SRC/prompt_yes_no.sh)

or for older bash, use a temporary file, eg

sed 's|/dev/tty|/dev/stdout|g' <$SH_PATH_SRC/prompt_yes_no.sh >/tmp/file
source /tmp/file
meuh
  • 51,383
  • If I do that, I get a "prompt_yes_no: command not found", but when inspecting the output of the sed command I don't see any error... – Sylordis Jul 20 '16 at 15:41
  • Owh... seems it only works in bash 4 according to this topic. Seems I'm screwed since I'm forced to use 2.05b. – Sylordis Jul 20 '16 at 15:52
  • You can use a temporary file instead. Do the sed into it: sed ...<... >/tmp/so then source /tmp/so. – meuh Jul 20 '16 at 16:28