Search code examples
linuxbashterminaltext-cursor

Linux bash: strange behavior after getting terminal cursor position


I wrote a shell script to gather and show some information after a successful login. However some info is taking some time to gather so I print to the terminal (ssh putty) ahead some headers and already available info before go back and print the delayed info into the right place.

To accomplish that I used the following script to get the current cursor position, (ignoring all the boring stuff that come before. It's a bunch of printf's, cat and cut's...

. ...
. ...
printf "^[[0m""\n"

# Get current settings.
if ! termios="$(stty -g 2>/dev/null)" ; then
    echo "Not running in a terminal." >&2
    exit 1
fi

# Restore terminal settings when the script exits.
trap "stty '$termios'" EXIT

# Disable ICANON ECHO. Should probably also disable CREAD.
stty -icanon -echo

# Request cursor coordinates
printf '\033[6n'

# Read response from standard input; note, it ends at R, not at newline
read -d "R" rowscols

# Clean up the rowscols (from \033[rows;cols -- the R at end was eaten)
rowscols="${rowscols//[^0-9;]/}"
rowscols=("${rowscols//;/ }")
#printf '(row %d, column %d)\n' ${rowscols[0]} ${rowscols[1]}    *<-- commented by me*

# Reset original terminal settings.
stty "$termios"

# To the stuff...
printf '(row %d, column %d)\n' ${rowscols[0]} ${rowscols[1]}

line=${rowscols[0]}
line=$(($line - 10))                        *<--- Indeed script's line 102. I want subtract 10*
col=56
printf '(r= %d, c= %d)\n' ${line} ${col}    *<--- Printed two times, both times wrong values*

exit 1      *<--- Put here just to exit earlier*


## Get uptime/activetime formated to my taste.
m_activetime=$(/usr/bin/activetime -v)
printf "\33[%d;%dH^[[38;5;196m ${m_activetime}" ${line} ${col}
. ...
. ...

When I run the code i get:

. ...
. ...
. ...
    ||=-= _ |-=-   |+++++++| _    ||= _   |            :
`~‾‾ '--~~__|- =   |+++++__|----~‾  ‾~`---',  CPU stat⸱:
             ~---__|,--~'                     Weather⸱⸱:

(row 16, column 1)
./c.asc: line 102: 16 1 - 10: syntax error in expression (error token is "1 - 10")
(r= 16, c= 1)
(r= 56, c= 0)
lr@pi:~ $

1) Script is bash (shebang #!/usr/bash)

2) The line (row 16, column 1) seems OK!

3) The script is called c.asc

4) I wonder what the heck that error is, I've used similar expressions before, not with bash arrays but even so...

line 102: 16 1 - 10: syntax error I can guess the 16, but where did it come the 1 - 10 ?

(error token is "1 - 10") what token "1 - 10" ????!!!

5) The first (r= 16, c= 1) is already wrong, it should be (r= 6, c= 56). Why is this? What happened to the subtraction of 10? Where did it go the value of the variable col?

6) Even more strange. I didn't instruct to print a second time, even so, now the variable line has an identity crisis and display the col value, and in both cases the instruction col=56 seems to have been ignored. Why and how did the variable line get the value of the variable col? Why did the variable col shift from the wrong value 1, to the wrong value 0?

7) The showed script has been transformed to track the error. It started by not printing into the expected position, and display errors. Also a version of the the printf printf '(r= %d, c= %d)\n' $((${line} - 10)) ${col} display equally similar and bizarre error.


p.s.

After some additional experiments with only the part of the script to get the terminal cursor position, it seems also that it is not completely sane. It returns the position alright but trying things like read r c < <(curspos), (assuming that curspos is the name of the script that return the tuple lin col), the prompt hangs until Ctrl-C is pressed and after that prompt goes crazy.

Thank you


Solution

  • The issue is that you're quoting the value to the array.

    rowscols=("${rowscols//;/ }")
    

    This tells bash to ignore the spaces and consider it as one value. So when you get the first value with ${rowscols[0]} later, you actually get 16 1 instead of 16 and there's no second value.

    It also worked with this printf because you didn't quote the values there.

    printf '(row %d, column %d)\n' ${rowscols[0]} ${rowscols[1]}
    

    I don't know why it ran the last printf twice, but it seems to be solved with the quoting.