5

I'd like to save then restore the current stty settings in a script that is also consuming stdin however stty -g is complaining about it:

stty: 'standard input': Inappropriate ioctl for device

I've tried closing the stdin file descriptor and calling stty in a subshell with overridden FDs. I can't figure out how to separate stdin from stty -g and I'd appreciate help or advice.

Note, I'm specifically interested in POSIX compatibility. No Bash/Zsh-isms, please.

Minimal script to reproduce the problem:

#!/usr/bin/env sh

# Save this so we can restore it later:
saved_tty_settings=$(stty -g)
printf 'stty settings: "%s"\n' "$saved_tty_settings"

# ...Script contents here that do something with stdin.

# Restore settings later
# (not working because the variable above is empty):
stty "$saved_tty_settings"

Run with print 'foo\nbar\n' | ./sttytest to view the error.

6
  • 2
    Maybe saved_tty_settings=$(stty -g < /dev/tty)?
    – icarus
    Sep 21, 2019 at 3:47
  • Nope. Did you try it and it works on your machine or are you just guessing?
    – whiteinge
    Sep 21, 2019 at 4:59
  • 1
    @whiteinge, the test command print 'foo\nbar\n' won't work; the correct command is printf 'foo\nbar\n' . Sep 21, 2019 at 9:16
  • 1
    And @whiteinge, you are supposed to redirect from /dev/tty at the restoring step too, or else stty will still bail when restoring states; see my answer. Sep 21, 2019 at 9:18
  • 1
    I actually tried echo hello | bash -c 'A=$(stty -g ) ; echo $A' and echo hello | bash -c 'A=$(stty -g </dev/tty) ; echo $A'. which got me the complaint about ioctl and the correct value in the 2 cases.
    – icarus
    Sep 21, 2019 at 14:13

1 Answer 1

8

@icarus' comment:

Maybe saved_tty_settings=$(stty -g < /dev/tty)?

is actually pointing to the right direction, but it is not the end of story.

You will need to apply the same redirection when restoring stty states too. Or else, you will still get Invalid argument or ioctl failure at the restoring stage...

Correct course of action:

saved_tty_settings="$(stty -g < /dev/tty)"

# ...do terminal-changing stuff...

stty "$saved_tty_settings" < /dev/tty

This is the actual script I tested; which I rewrote the whole thing as a classic Bourne shell script:

#!/bin/sh
# This is sttytest.sh

# This backs up current terminal settings...
STTY_SETTINGS="`stty -g < /dev/tty`"
echo "stty settings: $STTY_SETTINGS"

# This reads from standard input...
while IFS= read LINE
do
    echo "input: $LINE"
done

# This restores terminal settings...
if stty "$STTY_SETTINGS" < /dev/tty
then
    echo "stty settings has been restored sucessfully."
fi

The test run:

printf 'This\ntext\nis\nfrom\na\nredirection.\n' | sh sttytest.sh

The result:

stty settings: 2d02:5:4bf:8a3b:3:1c:7f:15:4:0:1:ff:11:13:1a:ff:12:f:17:16:ff:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
input: This
input: text
input: is
input: from
input: a
input: redirection.
stty settings has been restored sucessfully.

Tested using Debian Almquist Shell dash 0.5.7, and GNU Coreutils 8.13's stty.

1
  • Great answer. Thank you for taking the time to step through a full, working explanation!
    – whiteinge
    Sep 21, 2019 at 15:46

You must log in to answer this question.

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