├── LICENSE.md ├── README.md ├── current_pane_hostname.tmux └── scripts ├── host.sh ├── host_short.sh ├── pane_ssh_connect.sh ├── pane_ssh_connected.sh ├── port.sh ├── shared.sh ├── user.sh └── utils └── tmux.sh /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Antoine Bluchet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tmux current pane hostname/user 2 | 3 | Tmux plugin that enables displaying hostname and user of the current pane in your status bar. 4 | 5 | > [!IMPORTANT] 6 | > Replaces the `#H` default format variable 7 | 8 | ## Usage 9 | 10 | ### Basics 11 | 12 | - `#H` (`#{hostname}`) will be the hostname of your current path 13 | - `#{hostname_short}` will be the short hostname of your current path (up to the first dot) 14 | - `#U` (`#{username}`) will be current user name 15 | 16 | ### Remote connection info 17 | 18 | Plugin can detect pane has remote shell connection in several states: 19 | - ssh session inside pane 20 | - running docker (or podman) container 21 | 22 | In both cases, `#{hostname}` and `#{username}` with show their values relatively to that state. 23 | 24 | Besides `#{hostname}` and `#{username}` there are more usefull format variables: 25 | 26 | - `#{pane_ssh_port}` will show the connection port, otherwise it will be empty. 27 | - `#{pane_ssh_connected}` will be set to 1 if the currently selected pane has an active connection. (Useful for `#{?#{pane_ssh_connected},ssh,no-ssh}` which will evaluate to `ssh` if there is an active remote session in the currently selected pane and `no-ssh` otherwise.) 28 | - `#{pane_ssh_connect}` if an open remote session exists will show the connection info in `"username@hostname:port"` format, otherwise it will be empty. 29 | 30 | ### Example 31 | 32 | ```tmux 33 | set -g status-left " #[bg=blue]#U#[bg=red]@#H#{?#{pane_ssh_port},:#{pane_ssh_port},}#[default] " 34 | ``` 35 | 36 | ## Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended) 37 | 38 | Add plugin to the list of TPM plugins in `.tmux.conf`: 39 | 40 | ```tmux 41 | set -g @plugin "soyuka/tmux-current-pane-hostname" 42 | ``` 43 | 44 | Hit `prefix + I` to fetch the plugin and source it. 45 | 46 | `#U@#H` interpolation should now take the current pane ssh status into consideration. 47 | 48 | ## Manual Installation 49 | 50 | Clone the repo: 51 | 52 | $ git clone https://github.com/soyuka/tmux-current-pane-hostname ~/clone/path 53 | 54 | Add this line to the bottom of `.tmux.conf`: 55 | 56 | run-shell ~/clone/path/current_pane_hostname.tmux 57 | 58 | Reload TMUX environment: 59 | 60 | # type this in terminal 61 | $ tmux source-file ~/.tmux.conf 62 | 63 | `#U@#H` interpolation should now work. 64 | 65 | ## Todo 66 | 67 | - implement templating for `#{pane_ssh_connect}` 68 | 69 | ## Limitations 70 | 71 | - only named running container may be defined, otherwise it would be ignored 72 | 73 | ## License 74 | 75 | [MIT](LICENSE.md) 76 | -------------------------------------------------------------------------------- /current_pane_hostname.tmux: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | # Create map placeholders to handle scripts 6 | # to provide one handler for multiple placeholders. 7 | # Use '//' as separator, due to unix limitation in filenames 8 | placeholders_to_scripts=( 9 | "\#U//#($CURRENT_DIR/scripts/user.sh)" 10 | "\#\{username\}//#($CURRENT_DIR/scripts/user.sh)" 11 | "\#H//#($CURRENT_DIR/scripts/host.sh)" 12 | "\#\{hostname\}//#($CURRENT_DIR/scripts/host.sh)" 13 | "\#\{hostname_short\}//#($CURRENT_DIR/scripts/host_short.sh)" 14 | "\#\{pane_ssh_port\}//#($CURRENT_DIR/scripts/port.sh)" 15 | "\#\{pane_ssh_connected\}//#($CURRENT_DIR/scripts/pane_ssh_connected.sh)" 16 | "\#\{pane_ssh_connect\}//#($CURRENT_DIR/scripts/pane_ssh_connect.sh)") 17 | 18 | 19 | source $CURRENT_DIR/scripts/utils/tmux.sh 20 | 21 | do_interpolation() { 22 | local interpolated=$1 23 | for assignment in ${placeholders_to_scripts[@]}; do 24 | # ${assignment%\/\/*} - remove from // til EOL 25 | # ${assignment#*\/\/} - remove from BOL til // 26 | # ${interpolated//A/B} - replace all occurrences of A in interpolated with B 27 | local interpolated=${interpolated//${assignment%\/\/*}/${assignment#*\/\/}} 28 | done 29 | echo "$interpolated" 30 | } 31 | 32 | update_tmux_option() { 33 | local option=$1 34 | local option_value=$(get_tmux_option "$option") 35 | local new_option_value=$(do_interpolation "$option_value") 36 | set_tmux_option "$option" "$new_option_value" 37 | } 38 | 39 | main() { 40 | update_tmux_option "status-right" 41 | update_tmux_option "status-left" 42 | } 43 | 44 | main 45 | -------------------------------------------------------------------------------- /scripts/host.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/shared.sh 6 | 7 | main() { 8 | get_info host 9 | } 10 | 11 | main 12 | -------------------------------------------------------------------------------- /scripts/host_short.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/shared.sh 6 | 7 | main() { 8 | get_info host_short 9 | } 10 | 11 | main 12 | -------------------------------------------------------------------------------- /scripts/pane_ssh_connect.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/utils/tmux.sh 6 | source $CURRENT_DIR/shared.sh 7 | 8 | main() { 9 | local user host port 10 | IFS='#' read -r user host port <<< "$(get_info)" 11 | echo "${user}${host:+@$host}${port:+:$port}" 12 | 13 | # @TODO user-formatted pane_ssh_connect output 14 | # eval is literaly evil, so i won't use this 15 | # eval echo $(get_tmux_option $pane_ssh_connect_format) 16 | # get_tmux_option $pane_ssh_connect_format | sed \ 17 | # -e "s/%{user}/$user/" \ 18 | # -e "s/%{host}/$host/" \ 19 | # -e "s/%{port}/$port/" 20 | } 21 | 22 | main 23 | -------------------------------------------------------------------------------- /scripts/pane_ssh_connected.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/shared.sh 6 | 7 | main() { 8 | ssh_connected && echo 1 || echo 0 9 | } 10 | 11 | main 12 | -------------------------------------------------------------------------------- /scripts/port.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/shared.sh 6 | 7 | main() { 8 | get_info port 9 | } 10 | 11 | main 12 | -------------------------------------------------------------------------------- /scripts/shared.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | get_info() { 4 | local host port user 5 | local cmd=$(__current_pane_command) 6 | if __ssh_cmd "$cmd"; then 7 | cmd=$(echo "$cmd" | grep -E 'ssh' | sed -E 's/^.*ssh //') 8 | IFS=' ' read -r host port user <<<$(__get_remote_info "$cmd") 9 | elif __containered_cmd "$cmd"; then 10 | cmd=$(echo "$cmd" | grep -E 'docker|podman' | sed -E 's/^[[:blank:]]* //') 11 | IFS=' ' read -r host user <<<$(__get_container_info "$cmd") 12 | else 13 | IFS=' ' read -r host user <<<$(__get_local_info) 14 | fi 15 | # Set defaults 16 | user="${user:-$(__get_username)}" 17 | host="${host:-$(__get_hostname)}" 18 | 19 | # Return requested info 20 | case "$1" in 21 | "user") 22 | echo "$user" 23 | ;; 24 | "host") 25 | echo "$host" 26 | ;; 27 | "host_short") 28 | [[ -z "$host" ]] && __get_hostname_short || echo "$host" 29 | ;; 30 | "port") 31 | echo "$port" 32 | ;; 33 | *) 34 | echo "${user}#${host}#${port}" 35 | ;; 36 | esac 37 | } 38 | 39 | ssh_connected() { 40 | local cmd=$(__current_pane_command) 41 | __ssh_cmd "$cmd" 42 | } 43 | 44 | containered() { 45 | local cmd=$(__current_pane_command) 46 | __containered_cmd "$cmd" 47 | } 48 | 49 | __ssh_cmd() { 50 | [[ $1 =~ (^|^[[:blank:]]*|/?)ssh[[:blank:]] ]] || [[ $1 =~ (^|^[[:blank:]]*|/?)sshpass[[:blank:]] ]] 51 | } 52 | 53 | __containered_cmd() { 54 | [[ $1 =~ (^|^[[:blank:]]*|/?)docker[[:blank:]] ]] || [[ $1 =~ (^|^[[:blank:]]*|/?)podman[[:blank:]] ]] 55 | } 56 | 57 | __current_pane_command() { 58 | local ppid=$(tmux display-message -p "#{pane_pid}") 59 | local pid command cmd 60 | 61 | while [[ -n "$ppid" ]] ; do 62 | IFS=' ' read -r ppid pid command <<<$(ps -o ppid=,pid=,command= | grep -E "^[[:blank:]]*$ppid") 63 | [[ -z "$command" ]] && break 64 | # @hack in case of ProxyJump, ssh spawns a new ssh connection to jump host as child process 65 | # in that case, check if both parent and child processes are ssh, select parent one's cmd 66 | __ssh_cmd "$cmd" && __ssh_cmd "$command" && break 67 | ppid="$pid" 68 | cmd="$command" 69 | done 70 | 71 | echo "$cmd" 72 | } 73 | 74 | __get_remote_info() { 75 | local cmd="$1" 76 | # Fetch configuration with given cmd 77 | # Depending of ssh version, configuration output may or may not contain `host` directive 78 | # Check both `host` and `hostname` for old ssh versions compatibility and prefer `host` if exists 79 | ssh -TGN $cmd 2>/dev/null | grep -E -e '^host(name)?\s' -e '^port\s' -e '^user\s' | sort --unique --key 1,1.4 | cut -f 2 -d ' ' | xargs 80 | } 81 | 82 | __get_container_info() { 83 | local cmd="$1" 84 | 85 | local runner=${cmd%% *} 86 | if [[ $cmd =~ ' --name' ]]; then 87 | local container=$(echo ${cmd##* --name} | cut -f 1 -d ' ') 88 | container=${container##*=} 89 | else 90 | # @TODO get dynamic named container 91 | # local all_running_containers=$($runner ps -q | xargs $runner inspect) 92 | __get_local_info 93 | return 94 | fi 95 | 96 | local format 97 | case $runner in 98 | 'docker') 99 | format='{{ .Config.Hostname }}/{{ .Config.Domainname }}/{{ .Config.User }}' 100 | ;; 101 | 'podman') 102 | format='{{ .Config.Hostname }}/{{ .Config.DomainName }}/{{ .Config.User }}' 103 | ;; 104 | esac 105 | 106 | local info=$($runner inspect --format "$format" "$container") 107 | local host=$(echo $info | cut -f 1 -d '/') 108 | local domain=$(echo $info | cut -f 2 -d '/') 109 | local user=$(echo $info | cut -f 3 -d '/') 110 | 111 | echo "${host}${domain:+.$domain} ${user}" 112 | } 113 | 114 | __get_local_info() { 115 | local user=$(__get_username) 116 | local host=$(__get_hostname) 117 | 118 | echo "${host} ${user}" 119 | } 120 | 121 | __get_username() { 122 | command -v whoami > /dev/null && whoami 123 | } 124 | 125 | __get_hostname() { 126 | command -v hostname > /dev/null && hostname || echo "${HOSTNAME}" 127 | } 128 | 129 | __get_hostname_short() { 130 | command -v hostname > /dev/null && hostname --short || echo "${HOSTNAME%%.*}" 131 | } 132 | -------------------------------------------------------------------------------- /scripts/user.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | source $CURRENT_DIR/shared.sh 6 | 7 | main() { 8 | get_info user 9 | } 10 | 11 | main 12 | -------------------------------------------------------------------------------- /scripts/utils/tmux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # @TODO user-formatted pane_ssh_connect output 4 | declare -r pane_ssh_connect_format='@pane-ssh-connect-format' 5 | 6 | get_tmux_option() { 7 | local option=$1 8 | local default_value=$2 9 | local option_value=$(tmux show-option -gqv "$option") 10 | [[ -z "$option_value" ]] && echo "$default_value" || echo "$option_value" 11 | } 12 | 13 | set_tmux_option() { 14 | local option=$1 15 | local value=$2 16 | tmux set-option -gq "$option" "$value" 17 | } 18 | 19 | --------------------------------------------------------------------------------