preexec.bash (6317B)
1 # Copyright (c) 2008-2012 undistract-me developers. See LICENSE for details. 2 # 3 # preexec.bash -- Bash support for ZSH-like 'preexec' and 'precmd' functions. 4 5 # The 'preexec' function is executed before each interactive command is 6 # executed, with the interactive command as its argument. The 'precmd' 7 # function is executed before each prompt is displayed. 8 9 # To use, in order: 10 11 # 1. source this file 12 # 2. define 'preexec' and/or 'precmd' functions (AFTER sourcing this file), 13 # 3. as near as possible to the end of your shell setup, run 'preexec_install' 14 # to kick everything off. 15 16 # Note: this module requires 2 bash features which you must not otherwise be 17 # using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. preexec_install 18 # will override these and if you override one or the other this _will_ break. 19 20 # This is known to support bash3, as well as *mostly* support bash2.05b. It 21 # has been tested with the default shells on MacOS X 10.4 "Tiger", Ubuntu 5.10 22 # "Breezy Badger", Ubuntu 6.06 "Dapper Drake", and Ubuntu 6.10 "Edgy Eft". 23 24 25 # Copy screen-run variables from the remote host, if they're available. 26 27 if [[ "$SCREEN_RUN_HOST" == "" ]] 28 then 29 SCREEN_RUN_HOST="$LC_SCREEN_RUN_HOST" 30 SCREEN_RUN_USER="$LC_SCREEN_RUN_USER" 31 fi 32 33 # Default do-nothing implementation of preexec. 34 function preexec () { 35 true 36 } 37 38 # Default do-nothing implementation of precmd. 39 function precmd () { 40 true 41 } 42 43 # This function is installed as the PROMPT_COMMAND; it is invoked before each 44 # interactive prompt display. It sets a variable to indicate that the prompt 45 # was just displayed, to allow the DEBUG trap, below, to know that the next 46 # command is likely interactive. 47 function preexec_invoke_cmd () { 48 precmd 49 trap 'preexec_invoke_exec' DEBUG 50 } 51 52 # This function is installed as the DEBUG trap. It is invoked before each 53 # interactive prompt display. Its purpose is to inspect the current 54 # environment to attempt to detect if the current command is being invoked 55 # interactively, and invoke 'preexec' if so. 56 function preexec_invoke_exec () { 57 if [[ -n "$COMP_LINE" ]] 58 then 59 # We're in the middle of a completer. This obviously can't be 60 # an interactively issued command. 61 return 62 fi 63 trap '' DEBUG 64 65 if [[ "preexec_invoke_cmd" == "$BASH_COMMAND" ]] 66 then 67 # Sadly, there's no cleaner way to detect two prompts being displayed 68 # one after another. This makes it important that PROMPT_COMMAND 69 # remain set _exactly_ as below in preexec_install. Let's switch back 70 # out of interactive mode and not trace any of the commands run in 71 # precmd. 72 73 # Given their buggy interaction between BASH_COMMAND and debug traps, 74 # versions of bash prior to 3.1 can't detect this at all. 75 return 76 fi 77 78 # In more recent versions of bash, this could be set via the "BASH_COMMAND" 79 # variable, but using history here is better in some ways: for example, "ps 80 # auxf | less" will show up with both sides of the pipe if we use history, 81 # but only as "ps auxf" if not. 82 local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`; 83 84 # If none of the previous checks have earlied out of this function, then 85 # the command is in fact interactive and we should invoke the user's 86 # preexec hook with the running command as an argument. 87 preexec "$this_command" 88 } 89 90 function preexec_set_exit () { 91 __preexec_exit_status=$? 92 } 93 94 # Execute this to set up preexec and precmd execution. 95 function preexec_install () { 96 97 # *BOTH* of these options need to be set for the DEBUG trap to be invoked 98 # in ( ) subshells. This smells like a bug in bash to me. The null stderr 99 # redirections are to quiet errors on bash2.05 (i.e. OSX's default shell) 100 # where the options can't be set, and it's impossible to inherit the trap 101 # into subshells. 102 103 set -o functrace > /dev/null 2>&1 104 shopt -s extdebug > /dev/null 2>&1 105 106 # Finally, install the actual traps. 107 if [ -n "$PROMPT_COMMAND" ]; then 108 PROMPT_COMMAND="preexec_set_exit;${PROMPT_COMMAND} preexec_invoke_cmd"; 109 else 110 PROMPT_COMMAND="preexec_set_exit;preexec_invoke_cmd"; 111 fi 112 } 113 114 # Since this is the reason that 99% of everybody is going to bother with a 115 # pre-exec hook anyway, we'll include it in this module. 116 117 # Change the title of the xterm. 118 function preexec_xterm_title () { 119 local title="$1" 120 echo -ne "\033]0;$title\007" > /dev/stderr 121 } 122 123 function preexec_screen_title () { 124 local title="$1" 125 echo -ne "\033k$1\033\\" > /dev/stderr 126 } 127 128 # Abbreviate the "user@host" string as much as possible to preserve space in 129 # screen titles. Elide the host if the host is the same, elide the user if the 130 # user is the same. 131 function preexec_screen_user_at_host () { 132 local RESULT="" 133 if [[ "$SCREEN_RUN_HOST" == "$SCREEN_HOST" ]] 134 then 135 return 136 else 137 if [[ "$SCREEN_RUN_USER" == "$USER" ]] 138 then 139 echo -n "@${SCREEN_HOST}" 140 else 141 echo -n "${USER}@${SCREEN_HOST}" 142 fi 143 fi 144 } 145 146 function preexec_xterm_title_install () { 147 # These functions are defined here because they only make sense with the 148 # preexec_install below. 149 function precmd () { 150 preexec_xterm_title "${TERM} - ${USER}@${SCREEN_HOST} `dirs -0` $PROMPTCHAR" 151 if [[ "${TERM}" == screen ]] 152 then 153 preexec_screen_title "`preexec_screen_user_at_host`${PROMPTCHAR}" 154 fi 155 } 156 157 function preexec () { 158 preexec_xterm_title "${TERM} - $1 {`dirs -0`} (${USER}@${SCREEN_HOST})" 159 if [[ "${TERM}" == screen ]] 160 then 161 local cutit="$1" 162 local cmdtitle=`echo "$cutit" | cut -d " " -f 1` 163 if [[ "$cmdtitle" == "exec" ]] 164 then 165 local cmdtitle=`echo "$cutit" | cut -d " " -f 2` 166 fi 167 if [[ "$cmdtitle" == "screen" ]] 168 then 169 # Since stacked screens are quite common, it would be nice to 170 # just display them as '$$'. 171 local cmdtitle="${PROMPTCHAR}" 172 else 173 local cmdtitle=":$cmdtitle" 174 fi 175 preexec_screen_title "`preexec_screen_user_at_host`${PROMPTCHAR}$cmdtitle" 176 fi 177 } 178 179 preexec_install 180 }