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