How to get the cursor position in bash?

后端 未结 6 2176
遥遥无期
遥遥无期 2020-11-27 02:46

In a bash script, I want to get the cursor column in a variable. It looks like using the ANSI escape code {ESC}[6n is the only way to get it, for example the fo

相关标签:
6条回答
  • 2020-11-27 03:13

    You have to resort to dirty tricks:

    #!/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 "\033[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))
    
    0 讨论(0)
  • 2020-11-27 03:16

    The tput commands are what you need to use. simple, fast, no output to the screen.

    #!/bin/bash
    col=`tput col`;
    line=`tput line`;
    
    0 讨论(0)
  • 2020-11-27 03:20

    In the interests of portability I've had a go at making a vanilla POSIX-compatible version that will run in shells like dash:

    #!/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 $col,$row
    

    ...but I can't seem to find a viable alternative for bash's 'read -d'. Without the sleep, the script misses the return output entirely...

    0 讨论(0)
  • In case anyone else is looking for this, I came across another solution here: https://github.com/dylanaraps/pure-bash-bible#get-the-current-cursor-position

    Below is a slightly modified version with comments.

    #!/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
    
    0 讨论(0)
  • 2020-11-27 03:31

    You could tell read to work silently with the -s flag:

    echo -en "\E[6n"
    read -sdR CURPOS
    CURPOS=${CURPOS#*[}
    

    And then CURPOS is equal to something like 21;3.

    0 讨论(0)
  • 2020-11-27 03:33

    My (two) version of same...

    As a function, setting specific variable, using ncurses's user defined comands:

    getCPos () { 
        local v=() t=$(stty -g)
        stty -echo
        tput u7
        IFS='[;' read -rd R -a v
        stty $t
        CPos=(${v[@]:1})
    }
    

    Than now:

    getCPos 
    echo $CPos
    21
    echo ${CPos[1]}
    1
    echo ${CPos[@]}
    21 1
    
    declare -p CPos
    declare -a CPos=([0]="48" [1]="1")
    

    Nota: I use ncurses command: tput u7 at line #4 in the hope this will stay more portable than using VT220 string by command: printf "\033[6n"... Not sure: anyway this will work with any of them:

    getCPos () { 
        local v=() t=$(stty -g)
        stty -echo
        printf "\033[6n"
        IFS='[;' read -ra v -d R
        stty $t
        CPos=(${v[@]:1})
    }
    

    will work exactly same, while under VT220 compatible TERM.

    More info

    You may found some doc there:

    VT220 Programmer Reference Manual - Chapter 4

    4.17.2 Device Status Report (DSR)

    ...

    Host to VT220 (Req 4 cur pos)  CSI 6 n       "Please report your cursor position using a CPR (not DSR) control sequence."
      
    VT220 to host (CPR response)   CSI Pv; Ph R  "My cursor is positioned at _____ (Pv); _____ (Ph)."
                                                  Pv =  vertical position (row)
                                                  Ph =  horizontal position (column)
    
    0 讨论(0)
提交回复
热议问题