0

It took me 10 hours of searching the net, and testing techniques to get the results that worked on any shell (#!/bin/sh).

In BASH this is relatively simple, because read can be told how many characters are to be grabbed, and if a delimiter is found it will not wait to exit.

stty -icanon -echo; echo -en "\033[6n"; read -d R -n 12 ESCPOS; stty "$x_TERM"; \
ESCPOS=`echo "$ESCPOS" | tail -c +3`; echo "$ESCPOS"

How to write a sh script version, compatible with any shell?

4

Copied from https://unix.stackexchange.com/a/88327

Assuming your /bin/sh is a POSIX sh (on Solaris 10 and older which had a Bourne shell there instead, use /usr/xpg4/bin/sh instead):

if [ -t 0 ] && [ -t 1 ]; then
  old_settings=$(stty -g) || exit
  stty -icanon -echo min 0 time 3 || exit
  printf '\033[6n'
  pos=$(dd count=1 2> /dev/null)
  pos=${pos%R*}
  pos=${pos##*\[}
  x=${pos##*;} y=${pos%%;*}
  stty "$old_settings"
fi

That assumes the whole response will come in one go waiting for it for up to 0.3 seconds. That should be generally true for terminal emulators and pty devices, but not necessarily for terminals over serial. You could change it to min 8 time 3 to keep waiting (up to 0.3 second between each byte) until 8 bytes have been read, but with the drawback that it will take always at least 0.3 seconds if the answer is shorter than 8 bytes and would hang forever if there's no answer).

You could use awk -F'[^0-9]+' -v RS=R '{print $3, $2; exit}' with min 1 time 0. That would work with awk implementations other than mawk (which insists in accumulating a buffer full of data on input before starting to process it).

In the end, reading one byte at a time like you do in your own answer is the most reliable. You may want to add a timeout to account for terminals that don't send a response.

| improve this answer | |
  • the whole point of script in OP and my answer is that they dont have to wait, and that they dont know how many bytes (chars) to get. – Paul Wratt Aug 30 '17 at 3:37
  • who keeps upvoting this. the copied answer is NOT the right answer, read ALL the comments – Paul Wratt Nov 24 '17 at 0:04
2

Note:

unlike the wrongly copied and continually upvoted answer provided
(points scoring?), the following script IS NON-BLOCKING, and does
not care what length returned input may be.  IE it will work with 
ANY screen size.

With SH it is more complex, and I was unable to find enhanced command line version of the built-in read, eventually I found a mention of dd on STDIN, here is the result. NOTE that the SH version of built-in echo does not permit the use echo -en although /bin/echo -en does work we use printf instead.

#!/bin/sh
x_TERM=`stty -g`
stty -icanon -echo
printf "\033[6n"
ESCPOS=""
X=""
I=0
while [ ! "$X" = "R" ]; do
  X=`dd bs=1 count=1 2>/dev/null`
  I=`expr $I + 1`
  if [ $I -gt 2 -a ! "$X" = "R" ]; then
    ESCPOS="$ESCPOS$X"
  fi
done
stty "$x_TERM"
#echo "$ESCPOS"
CSRLIN=`echo "$ESCPOS" | cut -d \; -s -f 1`
POS=`echo "$ESCPOS" | cut -d \; -s -f 2`
echo "$CSRLIN"
#exit 0 <= dont use inline

I used the same code in two differnt scripts, one outputs CSRLIN, the other POS.

EDIT: you need to inline this script to use it in another script (eg . CSRLIN, as the shell has to be in interactive mode.

Cheers

Paul

| improve this answer | |
  • 1
    Note that POSIX states that implementations of echo shall not support any options, but results are implementation-defined if the first operand is -n or if any operand contains a backslash. A strictly conforming implementation could have echo -en "\033[6n" print the string -en \033[6n followed by a newline character (and FreeBSD's does exactly that). This is why one prefers printf to echo – Fox Aug 27 '17 at 4:54
  • -en \033[6n is what I got with BASH in SH mode. you can force in SH with /bin/echo -en "\033[6n" but what if the system does not have /bin. I believe its possible in SH mode to echo -ne "\033[6n" but again if its implementation dependent the script will fail, hence the use of printf here. – Paul Wratt Aug 27 '17 at 5:00
  • 1
    My comment was more to address the statement that "although/bin/echo -en does work" — the results are implementation defined in this case (because an operand contains backlash in your example). That is, it was mostly a warning to others describing why we prefer printf – Fox Aug 27 '17 at 5:07
  • 1
    For bash to be Unix compliant, you need to set the xpg_echo option in addition to the posix one (set when you run it as sh). On macOS (a Unix certified system), for /bin/sh, bash is built with the option enabled by default. A Unix echo on ASCII-based systems is required to output -en<SPC><ESC>[6n<LF>. It's unspecified per POSIX only (without XSI extension) because of the backslash as @Fox said. – Stéphane Chazelas Aug 27 '17 at 6:52
  • 1
    And for GNU echo (/bin/echo on GNU systems), the behaviour depends on whether POSIXLY_CORRECT is in the environment. See Why is printf better than echo? for more details. – Stéphane Chazelas Aug 27 '17 at 7:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.