user1934428
user1934428

Reputation: 22356

Reading answer to control string sent to xterm

An interactive program running in an xterm can send control codes to the xterm, which cause the xterm to respond by sending back an answer. I would like to read the answer. There are several of these control codes, so for the sake of this discussion, let's stick with the control sequence ESC Z, which causes the xterm to send back its terminal id. For instance, when I type in my shell (I'm using Zsh, but as far I can see, this applies to bash as well)

echo -e '\eZ'

I see in my terminal buffer the string

63;1;2;4;6;9;15;22;29c

and hear a beep (because the answer sent from xterm also contains non-printable characters, in particular it also contains an ESC). My goal is to somehow read this string 63;1;2;4;6;9;15;22;29c into a shell variable. I mainly use Zsh, but a solution in bash would be welcome as well (or in any other scripting language, such as Ruby, Perl or Python).

My first attempt was pretty straightforward:

#!/bin/zsh
echo -e '\eZ'
read -rs -k 25
echo $REPLY | xxd

This works, because I found out (with a little bit of trial and error) that the answerback string in this particular example on my particular xterm has a length of 25 characters. In the general case, of course, I don't know the exact length in advance, so I wanted a more flexible solution. The idea would be to read one character at a time, until nothing is left, and I wrote the following program to test my idea:

#!/bin/zsh
str=''
echo -e '\eZ'
while :
do
  read -rs -t -k
  if [[ -z $REPLY ]]
  then
    echo '(all read)'
    break
  fi
  str="${str}$REPLY"

done echo $str | xxd

However, this doesn't read anything. REPLY is always empty.

I also tried the variations read -rs -t -k 1 (same effect) and read -rs -k 1 (hangs forever). Even read -rs k 25 does not work anymore, so I guess the culprit is the while loop, not the read command. However, I do need a loop if I want to read the answerback string one character at a time.

Could someone explain, why my approach failed, and how I could solve my problem?

Upvotes: 3

Views: 829

Answers (3)

HappyFace
HappyFace

Reputation: 4133

Putting the answers here together, here is the finished product:

#!/usr/bin/env bash

tty=/dev/tty

cat >$tty
read -rs -t 0.2 -d "" <$tty
echo $REPLY | xxd -r -p
$ printf '\eP+q544e\e\\' | escape_code_answer_read.bash
TNxterm-kitty

Upvotes: 0

user1934428
user1934428

Reputation: 22356

Since I hadn't got any new findings for this question here, I have crossposted the problem at Unix/Linux Forums and received an answer for Zsh, and an improved answer for bash, which I would like to summarize here:

Assuming that tty contains the string denoting my terminal(following here the advice given by @ThomasDickey in his answer), the Zsh-command

read -rs -t 0.2 -k 1 <$tty # zsh

reads the next single character into the variable REPLY. It is important to note that -t must be given a timeout value, and the fact, that no character is available, can not be deduced from an empty REPLY, but from the exit code of the read command.

As for bash, there is an easier solution than the one I've posted in my comment: With

read -rs -t 0.2 -d "" <$tty # bash

the whole answerback string is read as once into REPLY; no loop is necessary.

Upvotes: 2

Thomas Dickey
Thomas Dickey

Reputation: 54583

You could make the read-statement read directly from the terminal, e.g.,

read -rs -t -k < $(tty)

to work around redirection of the shell.

Upvotes: 2

Related Questions