This might sound pretty weird, but I know how to set the vertical cursor position in Bash like this:
echo -e "e[12H"
This moves the cursor to the 12th line (starting with 1).
So how do I get the cursor position (line number) using linux bash? It would be helpful if I could simply store this value in a variable so I can calculate with it.
EDIT:
This is the error I get:
$ sh rowcol.sh
-en
read: 9: Illegal option -d
test.sh: 12: Bad substitution
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Using the -p option instead of echo I found solved the hanging problem in a script. Tested with GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).
IFS=';' read -sdR -p $'E[6n' ROW COL;echo "${ROW#*[}"
works interactively or in a script:
#!/usr/bin/env bash
function pos
{
local CURPOS
read -sdR -p $'E[6n' CURPOS
CURPOS=${CURPOS#*[} # Strip decoration characters <ESC>[
echo "${CURPOS}" # Return position in "row;col" format
}
function row
{
local COL
local ROW
IFS=';' read -sdR -p $'E[6n' ROW COL
echo "${ROW#*[}"
}
function col
{
local COL
local ROW
IFS=';' read -sdR -p $'E[6n' ROW COL
echo "${COL}"
}
tput sc # Save cursor position
tput cup 5 10 # Move to row 6 col 11
POS1=$(pos) # Get the cursor position
ROW1=$(row)
COL1=$(col)
tput cup 25 15 # Move to row 25 col 15
POS2=$(pos) # Get the cursor position
ROW2=$(row)
COL2=$(col)
tput rc # Restore cursor position
echo $BASH_VERSION
echo $POS1 $ROW1 $COL1
echo $POS2 $ROW2 $COL2
Outputs:
$./cursor.sh 3.00.16(1)-release 6;11 6 11 26;16 26 16
Method 2
I was able to use some of the examples from the same article on SO, titled: How to get the cursor position in bash?. I’m posting this here just to show that they work and that the contents of solutions is actually on U&L as well.
Bash solutions
From inside a script
#!/bin/bash
# based on a script from http://invisible-island.net/xterm/xterm.faq.html
exec < /dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
# on my system, the following line can be replaced by the line below it
echo -en "33[6n" > /dev/tty
# tput u7 > /dev/tty # when TERM=xterm (and relatives)
IFS=';' read -r -d R -a pos
stty $oldstty
# change from one-based to zero based so they work with: tput cup $row $col
row=$((${pos[0]:2} - 1)) # strip off the esc-[
col=$((${pos[1]} - 1))
echo "(row,col): $row,$col"
NOTE: I changed the output slightly!
Example
$ ./rowcol.bash (row,col): 43,0 $ clear $ ./rowcol.bash (row,col): 1,0
Interactive shell
This command chain worked for getting the row and column positions of the cursor:
$ echo -en "E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"
Example
$ echo -en "E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"
13;1
$ clear
$ echo -en "E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"
2;1
NOTE: This method doesn’t appear to be usable from any type of script. Even simple commands in an interactive terminal didn’t work for me. For example:
$ pos=$(echo -en "E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}")
just hangs indefinitely.
dash/sh solutions
From inside a script
This solution is for Ubuntu/Debian systems that come stock with dash, which is POSIX compliant. Because of this, the read command doesn’t support the -d switch among other differences.
To get around this there is this solution which uses a sleep 1 in place of the -d switch. This isn’t ideal but offers at least a working solution.
#!/bin/sh
exec < /dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
tput u7 > /dev/tty
sleep 1
IFS=';' read -r row col
stty $oldstty
row=$(expr $(expr substr $row 3 99) - 1) # Strip leading escape off
col=$(expr ${col%R} - 1) # Strip trailing 'R' off
echo "(row,col): $col,$row"
Example
$ ./rowcol.sh (row,col): 0,24 $ clear $ ./rowcol.sh (row,col): 0,1
Interactive shell
I couldn’t find a workable solution that worked for just sh in an interactive shell.
Method 3
You can get the cursor position via ANSI CSI DSR (Device Status Report): e[6n. Note it returns it in a format similar to ANSI CSR CUP (Cursor Position) that you mention in your question, however it follows the form e[n;mR (where n is the row and m the column).
More details of ANSI escape codes on wikipedia.
For getting hold of the value into a variable, this was answered on StackOverflow.
As mentioned in a previous answer/comment (and detailed in the wikipedia article), these codes are not always portable (from terminal to terminal and OS to OS). I still think this is better handled with termcap/curses 😉
Method 4
With POSIX sh syntax:
if [ -t 0 ] && [ -t 1 ]; then
old_settings=$(stty -g) || exit
trap 'stty "$(old_settings)"' INT TERM QUIT ALRM
stty -icanon -echo min 0 time 3 || exit
printf '33[6n'
pos=$(dd count=1 2> /dev/null)
pos=${pos%R*}
pos=${pos##*[}
x=${pos##*;} y=${pos%%;*}
stty "$old_settings"
trap - INT TERM QUIT ALRM
fi
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0