Thanks to Paulo Scardine for the heart of and explanation of the following code.
#!/usr/bin/env bash
echo -ne "\033[6n" # The ANSI code ESC[6n asks the terminal for the position.
# The terminal puts the answer in the input buffer, in the format ESC[n;mR, where n;m = row;col.
read -s -d\[ garbage # Read the input buffer up to the "[" ("s"ilently, without echoing what's read). Place this ESC[
# into a variable garbage, which we'll henceforth forget about; we don't need it.
# This leaves n;mR in the input buffer.
read -s -d ';' row # Read again, until we get to the character ';', storing the n in the variable row
read -s -d R col # Read again, until we get to the character 'R', storing the m in the variable col
echo "The (row,col) coords are ($row,$col)"
Printing the ANSI Escape Sequence ESC[6n asks the terminal for the current cursor position, which is returned to the input buffer in the format ESC[n;mR (which may sometimes be represented as ^[[n;mR). The read command reads from the input buffer, usually until a newline is encountered, but the -d <some_char> (delimiter) argument will make read stop at the specified character instead.
#!/usr/bin/env bash # Magic to read the current cursor position (origin 1,1) tput u7; read -t1 -srd'[' _; IFS=';' read -t1 -srd'R' row col printf "Row: %d\nCol: %d\n\n" $row $col
tput u7 uses the "user7" command of tput to essentially printout the \033[6n ANSI code.
The rest of the one-liner does pretty much the same as the more verbose version, with the _ being an "unused, not-named variable" equivalent to garbage.
This is essentially a mix of the two methods above.
#!/usr/bin/env bash
echo -ne "\033[6n" # Ask the terminal for the position.
# The terminal puts the answer in the input buffer,
# in the format ESC[row;col.
read -sd'[' _ # Read input, silently, until a '[' is reached as a stop-
# reading delimiter, storing it in a throwaway "variable".
IFS=';' # Change default stop-reading delim from newline to ";".
read -sd'R' row col # Read rest of input as two fields, up to "R".
printf "Row: %d\nCol: %d\n\n" $row $col
If you want to get the dimensions of the current window, just send the cursor to way beyond any expected col/row maximum; the cursor will stop at the screen's maximums, at which point you can then read the position.
#!/usr/bin/env bash
echo -ne "\033[10000;10000H" # Try to move cursor past any expected row/col maximums. Cursor will stop at the maximums.
# Then read the maximums.
echo -ne "\033[6n" # Ask the terminal for the position
# The response looks like ESC[n;mR - where n = row, m = col
read -s -d '[' _ # Read the response silently, discarding the first part of the response, leaving n;mR
read -s -d ';' rows # Read some more, silently, until we get to the character ';', storing what we read in the variable rows
read -s -d R cols # Read some more, silently, until we get to the character 'R', storing what we read in the variable cols
echo # Echo a newline to get the cursor back to the left side of the screen.
printf "The screen dimensions are %dX%d.\n" $rows $cols # Using 'printf' 'cause I didn't want to bother hunting how to get echo to print without spaces.
#!/usr/bin/env bash # Use "tput" to get dimensions rows=$(tput lines) cols=$(tput cols) printf "Rows: %d - Cols: %d\n" $rows $cols
#!/usr/bin/env bash
#
# curpos -- demonstrate a method for fetching the cursor position in bash
# modified version of https://github.com/dylanaraps/pure-bash-bible#get-the-current-cursor-position
#
#========================================================================================
#-
#- THE METHOD
#-
#- IFS='[;' read -p $'\e[6n' -d R -a pos -rs || echo "failed with error: $? ; ${pos[*]}"
#-
#- THE BREAKDOWN
#-
#- $'\e[6n' # escape code, {ESC}[6n;
#-
#- This is the escape code that queries the cursor postion. see XTerm Control Sequences (1)
#-
#- same as:
#- $ echo -en '\033[6n'
#- $ 6;1R # '^[[6;1R' with nonprintable characters
#-
#- read -p $'\e[6n' # read [-p prompt]
#-
#- Passes the escape code via the prompt flag on the read command.
#-
#- IFS='[;' # characters used as word delimiter by read
#-
#- '^[[6;1R' is split into array ( '^[' '6' '1' )
#- Note: the first element is a nonprintable character
#-
#- -d R # [-d delim]
#-
#- Tell read to stop at the R character instead of the default newline.
#- See also help read.
#-
#- -a pos # [-a array]
#-
#- Store the results in an array named pos.
#- Alternately you can specify variable names with positions: <NONPRINTALBE> <ROW> <COL> <NONPRINTALBE>
#- Or leave it blank to have all results stored in the string REPLY
#-
#- -rs # raw, silent
#-
#- -r raw input, disable backslash escape
#- -s silent mode
#-
#- || echo "failed with error: $? ; ${pos[*]}"
#-
#- error handling
#-
#- ---
#- (1) XTerm Control Sequences
#- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Functions-using-CSI-_-ordered-by-the-final-character_s_
#========================================================================================
#-
#- CAVEATS
#-
#- - if this is run inside of a loop also using read, it may cause trouble.
#- to avoid this, use read -u 9 in your while loop. See safe-find.sh (*)
#-
#-
#- ---
#- (2) safe-find.sh by l0b0
#- https://github.com/l0b0/tilde/blob/master/examples/safe-find.sh
#=========================================================================================
#================================================================
# fetch_cursor_position: returns the users cursor position
# at the time the function was called
# output "<row>:<col>"
#================================================================
fetch_cursor_position() {
local pos
IFS='[;' read -p $'\e[6n' -d R -a pos -rs || echo "failed with error: $? ; ${pos[*]}"
echo "${pos[1]}:${pos[2]}"
}
#----------------------------------------------------------------------
# print ten lines of random widths then fetch the cursor position
#----------------------------------------------------------------------
#
MAX=$(( $(tput cols) - 15 ))
for i in {1..10}; do
cols=$(( $RANDOM % $MAX ))
printf "%${cols}s" | tr " " "="
echo " $(fetch_cursor_position)"
done