Repository: vaniacer/sshto Branch: master Commit: 1eb51f406049 Files: 5 Total size: 37.2 KB Directory structure: gitextract_10rhdvse/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE.md ├── README.md └── sshto ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ custom: ['paypal.me/sshto'] ================================================ FILE: .gitignore ================================================ .idea /install.sh ================================================ FILE: LICENSE.md ================================================ License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # sshto [![Twitter Follow](https://img.shields.io/twitter/follow/Vaniacer?style=social)](https://twitter.com/Vaniacer) [![paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/sshto?locale.x=en_US) Feel free to support the project!)
Small bash script that builds a menu (via dialog) from your ~/.ssh/config.
![screeenshot](https://user-images.githubusercontent.com/18072680/60570513-69e99f00-9d7a-11e9-916d-48b74fa7585a.png)
Allows you to connect to your servers or run commands from menu. Available commands:
![cmds](https://user-images.githubusercontent.com/18072680/211161226-1c5eec5a-634b-4902-90cd-5947dd95083e.png)
Your commands can be easily added to this list. Just edit this part of the script:
cmdlist=(
    #Command#    #Description#
    "${slct[@]}" #De/Select command
    "Username"   "Change ssh username to \Z1$GUEST\Z0"
    "Add tab"    "Add terminal tab with \Z1sshto\Z0 for \Z4$target\Z0"
    "Ssh tab"    "Add terminal tab with \Z1ssh\Z0 to \Z4$target\Z0"
    ''           ''
    "ls -lah"    "List Files"
    "free -h"    "Show free memory"
    "df  -ih"    "Show free inodes"
    "df   -h"    "Show free disk space"
    "Custom"     "Run custom command on \Z4$target\Z0"
    "Script"     "Run custom script on \Z4$target\Z0"
    ''           ''
    'Yes'        "Say 'yes' to SSH"
    "Info"       "Full system info"
    'Fix_id'     "Update host in known_hosts"
    "Sshkey"     "Add my ssh key to \Z4$target\Z0"
    "Alias"      "Add my useful aliases to \Z4$target\Z0"
    "Copy"       "Copy selected file or dir to \Z4$target\Z0"
    ''           ''
    "Home"       "Change home folder \Z4$home\Z0 on local server"
    "Dest"       "Change destination folder \Z4$DEST\Z0 on \Z4$target\Z0"
    "Upload"     "Upload file or folder from \Z4$home\Z0 to \Z4$target:${DEST}\Z0"
    "Download"   "Download file or folder from \Z4$target:${DEST}\Z0 to \Z4$home\Z0"
    "Mount"      "Mount remote folder \Z4$target:$DEST\Z0 to \Z4$home\Z0"
    "Unmount"    "Unmount remote folder \Z4$target:$DEST\Z0 from \Z4$home\Z0"
    ''           ''
    "Local"      "Change local  port \Z1$LOCAL\Z0"
    "Remote"     "Change remote port \Z1$REMOTE\Z0"
    "Tunnel"     "Start portunneling from \Z4$target:$REMOTE\Z0 to \Z4localhost:$LOCAL\Z0"
    ''           ''
    "ShowConf"   "Show ssh config for this host"
    "EditConf"   "Edit ssh config for this host"
)
First collumn - command, second - description.
Simple commands like `ls -la` could be added as is.
A list of commands or a complicated logic should be added via function.
Empty values(`''`) could be used as a delimiter.
You can quick jump to the selected server via CONNECT button.
To close ssh session press CTRL+D or run `exit` command, it'll bring you back to sshto commands section.

Optional hosts description could be added like this:
Host server1 #Description, it could be more than one word
HostName 192.168.0.1
Port 22
User admin
Optional start menu delimiters '---{ Group Name }---' could be added like this:
#Host DUMMY #Group Name#
If you are unhappy with this 'DUMMY' group name template, or you actually have a host named 'dummy', you can change this template by ajusting this variable `group_id=dummy`.
All these additions won't break your *ssh configs* coz they are considered as comments. ------ ~/.ssh/config example:
#Host DUMMY #Rybinsk#

Host rybserver1 #First server
HostName localhost

Host rybserver2 #Second server
HostName localhost

Host rybserver3 #Third server
HostName localhost

#Host DUMMY #Moscow#

Host moserver1 #First server
HostName localhost

Host moserver2 #Second server
HostName localhost

Host moserver3 #Third server
HostName localhost
Script greps data from multiple config files via pattername `config*` in `~/.ssh` dir.
So you can split config to multiple files and use them with Include directive, example:
Include config_moscow
Include config_rybinsk
Include config*
All preset variables and functions could be tweaked via `~/.sshtorc` config file:
echo "REMOTE=9000  # Remote port for tunneling." >> ~/.sshtorc
You can customize dialog itself a bit by creating and editing its config file:
dialog --create-rc ~/.dialogrc
nano ~/.dialogrc
If you don't have dialog and don't want(or can't) to install it, there is a dialog-less version of sshto in my new project [bashui](https://github.com/vaniacer/bashui) here is how it looks: ![bashui-hosts](https://habrastorage.org/getpro/habr/upload_files/024/c74/38e/024c7438e6429f5e37a8a71d98bf7edb.png) ![bashui-commands](https://habrastorage.org/getpro/habr/upload_files/495/2cc/526/4952cc52616db16acfa7b2fd9e8d366f.png) Try it [bashui-sshto](https://github.com/vaniacer/bashui/blob/master/demo_sshto)! # How to install Clone\download this project, go to it's folder and run:
sudo cp sshto /usr/bin/

#and to unistall
sudo rm /usr/bin/sshto
See how it works at asciinema
ssh config guide
Tom Lawrens video guide about ssh config and sshto [![Twitter Follow](https://img.shields.io/twitter/follow/Vaniacer?style=social)](https://twitter.com/Vaniacer) [![paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/sshto?locale.x=en_US) Feel free to support the project!)
================================================ FILE: sshto ================================================ #!/usr/bin/env bash #--------------------------{ Default values }--------------------------------------------------------------------------- OPT=$1 # Options. sshdir=~/.ssh # Ssh configfiles folder KEY="$sshdir/id_rsa.pub" # SSH key to use in command Sshkey(Add my ssh key to host). REMOTE=8080 # Remote port for tunneling. LOCAL=18080 # Local port for tunneling. GUEST=$USER # Alternative username to login with. DEST='~' # Destination folder on target server to download\upload files and sshfs mount. TIME=60 # Timer for tunneling command. Tunnel will be closed after 60 seconds, but it will stay open if used. EDITOR=nano # SSH confile editor. LSEXIT=true # Perform ls on exit true|false. home=$PWD # Destination folder on local server to download\upload files and sshfs mount. sshfsopt= # Sshfs options if needed group_id=dummy # Group separator identification template, 'dummy' by default, don't ask why, historically) rsyncopt='-Pgzortl' # Upload/download rsync options knwhosts="$sshdir"/known_hosts # Path to known_hosts file. confile=~/.sshtorc # Path to config file, have to be chmod'ed to 600. tmpfile=/tmp/sshtorc-${USER} # Path to tmp file to save selected context. sshto_script[0]=~ # Path to sshto script sshto_script[1]=.sshto_script # Sshto script filename sshto_script[2]="${sshto_script[0]}/${sshto_script[1]}" # Sshto script full #------------------------{ Add some tabs }------------------------------------------------------------------------------ tabbed(){ target=$target gnome-terminal --title=$target --tab -qe "${1/_target_/$target}"; } # Terminal command for tabs #------------------------{ Add your commands to this lists }------------------------------------------------------------ cmdlist_renew(){ cmdlist=( #Command# #Description# "${slct[@]}" #De/Select command "Username" "Change ssh username to \Z1$GUEST\Z0" "Add tab" "Add terminal tab with \Z1sshto\Z0 for \Z4$target\Z0" "Ssh tab" "Add terminal tab with \Z1ssh\Z0 to \Z4$target\Z0" '' '' "ls -lah" "List Files" "free -h" "Show free memory" "df -ih" "Show free inodes" "df -h" "Show free disk space" "Custom" "Run custom command on \Z4$target\Z0" "Script" "Run custom script on \Z4$target\Z0" '' '' 'Yes' "Say 'yes' to SSH" "Info" "Full system info" 'Fix_id' "Update host in known_hosts" "Sshkey" "Add my ssh key to \Z4$target\Z0" "Alias" "Add my useful aliases to \Z4$target\Z0" "Copy" "Copy selected file or dir to \Z4$target\Z0" '' '' "Home" "Change home folder \Z4$home\Z0 on local server" "Dest" "Change destination folder \Z4$DEST\Z0 on \Z4$target\Z0" "Upload" "Upload file or folder from \Z4$home\Z0 to \Z4$target:${DEST}\Z0" "Download" "Download file or folder from \Z4$target:${DEST}\Z0 to \Z4$home\Z0" "Mount" "Mount remote folder \Z4$target:$DEST\Z0 to \Z4$home\Z0" "Unmount" "Unmount remote folder \Z4$target:$DEST\Z0 from \Z4$home\Z0" '' '' "Local" "Change local port \Z1$LOCAL\Z0" "Remote" "Change remote port \Z1$REMOTE\Z0" "Tunnel" "Start portunneling from \Z4$target:$REMOTE\Z0 to \Z4localhost:$LOCAL\Z0" '' '' "ShowConf" "Show ssh config for this host" "EditConf" "Edit ssh config for this host" ) cmdlist_group=( #Command# #Description# "${slct_grp[@]}" #De/Select command "Username" "Change ssh username to \Z1$GUEST\Z0" "Add tabs" "Add terminal tabs with \Z1sshto\Z0 for hosts in \Z4$group\Z0 group" "Ssh tabs" "Add terminal tabs with \Z1ssh\Z0 to hosts from \Z4$group\Z0 group" '' '' "ls -la" "List Files" "free -h" "Show free memory" "df -ih" "Show free inodes" "df -h" "Show free disk space" "Custom" "Run custom command on \Z4$group\Z0" "Script" "Run custom script on \Z4$group\Z0" '' '' 'Yes' "Say 'yes' to SSH" 'Fix_id' "Update hosts in known_hosts" "Info" "Full system info" "Alias" "Add my useful aliases to \Z4$group\Z0" "Copy" "Copy selected file or dir to \Z4$group\Z0" '' '' "Home" "Change home folder \Z4$home\Z0 on local server" "Dest" "Change destination folder \Z4$DEST\Z0 on \Z4$group\Z0" "Upload" "Upload file or folder from \Z4$home\Z0 to \Z4$group:${DEST}\Z0" '' '' "EditConf" "Edit ssh config for this group" ) } #--------------------------------------------------------------------+-------------------------------------------------- #Color picker, usage: printf ${BLD}${CUR}${RED}${BBLU}"Hello!)"${DEF}| #-------------------------+--------------------------------+---------+ # Text color | Background color | | #-----------+-------------+--------------+-----------------+ | # Base color|Lighter shade| Base color | Lighter shade | | #-----------+-------------+--------------+-----------------+ | BLK='\e[30m'; blk='\e[90m'; BBLK='\e[40m'; bblk='\e[100m' #| Black | RED='\e[31m'; red='\e[91m'; BRED='\e[41m'; bred='\e[101m' #| Red | GRN='\e[32m'; grn='\e[92m'; BGRN='\e[42m'; bgrn='\e[102m' #| Green | YLW='\e[33m'; ylw='\e[93m'; BYLW='\e[43m'; bylw='\e[103m' #| Yellow | BLU='\e[34m'; blu='\e[94m'; BBLU='\e[44m'; bblu='\e[104m' #| Blue | MGN='\e[35m'; mgn='\e[95m'; BMGN='\e[45m'; bmgn='\e[105m' #| Magenta | CYN='\e[36m'; cyn='\e[96m'; BCYN='\e[46m'; bcyn='\e[106m' #| Cyan | WHT='\e[37m'; wht='\e[97m'; BWHT='\e[47m'; bwht='\e[107m' #| White | #----------------------------------------------------------+---------+ # Effects | #--------------------------------------------------------------------+ DEF='\e[0m' #Default color and effects | BLD='\e[1m' #Bold\brighter | DIM='\e[2m' #Dim\darker | CUR='\e[3m' #Italic font | UND='\e[4m' #Underline | INV='\e[7m' #Inverted | COF='\e[?25l' #Cursor Off | CON='\e[?25h' #Cursor On | #--------------------------------------------------------------------+ # Text positioning, usage: XY 10 10 'Hello World!' | XY(){ printf "\e[$2;${1}H$3"; } #| # Print line, usage: line - 10 | line -= 20 | line 'Hello World!' 20 | line(){ printf -v _L %$2s; printf -- "${_L// /$1}"; } #| # Create sequence like {0..(X-1)}, usage: que 10 | que(){ printf -v _N %$1s; _N=(${_N// / 1}); printf "${!_N[*]}"; } #| #------------{ Check that dialog and gawk are installed }------------+ install_help=" ${BLD}sshto$DEF requires that the package '${GRN}%b$DEF' is installed. Type this into the terminal and press return: ${BLD}%b$DEF Then run ${BLD}sshto$DEF again " how_to_install(){ local package=$1 which yum &> /dev/null && installer="yum -y install $package" which brew &> /dev/null && installer="brew install $package" which apt-get &> /dev/null && installer="apt-get install -y $package" printf -- "$install_help" "$package" "$installer" [[ $2 ]] && exit $2 } for package in dialog gawk; { which $package &> /dev/null || how_to_install $package 1; } #------------------------{check bash version }-------------------------------------------------------------------------- tversion=4.2 cversion=${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]} gawk '{if($1>$2){exit 1}}' <<< "$tversion $cversion" || { printf "\nBASH version ${BLD}$tversion+$DEF required to run ${BLD}sshto$DEF, your is - $BLD$BASH_VERSION$DEF\n" exit 1 } #------------------------{ Waiting animation }-------------------------------------------------------------------------- cursor () { case $1 in on) stty echo; printf "$CON";; off) stty -echo; printf "$COF";; esac } x=$[COLUMNS/2-3] y=$[ LINES/2-3] sand=( ⠁ ⠂ ⠄ ' ' ) # { small digits } sd=(₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉) bs='⠴⠷⠦' # bottom sand pile ts='⠖' # top sand pile WAIT(){ clear; cursor off; i=0; start=$SECONDS XY $[x-1] $y $UND$BLD$RED' '$DEF # _______ XY $[x-1] $[y+1] $RED'╲'$DIM$UND' '$DEF$red'╱'$DEF # ╲_____╱ XY $x $[y+2] $BLU'( '$BLD$WHT'•'$BLD$BLU')'$DEF # ( •) XY $x $[y+3] $BLU' ╲'$YLW"$ts"$BLD$BLU'╱'$DEF # ╲⠖╱ XY $x $[y+4] $BLU" ╱$YLW${sand[$i]}$BLD$BLU╲"$DEF # ╱⠂╲ XY $x $[y+5] $BLU'('$YLW"$bs"$BLD$BLU')'$DEF # (⠴⠷⠦) XY $[x-1] $[y+6] $RED'╱'$RED'‾‾‾‾‾'$BLD$RED'╲'$DEF # ╱‾‾‾‾‾╲ XY $[x-1] $[y+7] $DIM$RED'‾‾‾‾‾‾‾'$DEF # ‾‾‾‾‾‾‾ ( while true; do sleep 0.07 printf -v counter "%03d" $[SECONDS-start] small="${sd[${counter:0:1}]}${sd[${counter:1:1}]}${sd[${counter:2:1}]}" XY $[x-1] $[y+1] $RED'╲'$DIM$UND" $small "$DEF$red'╱'$DEF XY $x $[y+4] $BLU" ╱$YLW${sand[$i]}$BLD$BLU╲"$DEF ((i++)); (($i==${#sand[@]})) && i=0; done ) & waiter=$! } GO() { [[ -e /proc/$waiter ]] && kill $waiter; cursor on; clear; } check_confile(){ [[ -e $confile ]] || return [[ $(stat -c "%a %U %G" "$confile") == "600 $USER $USER" ]] && . "$confile" } #------------------------{ Pause function }----------------------------------------------------------------------------- pause(){ local mess=${1:-'press any key to continue'} printf "\n$COF$BLD$mess\n" read -srn1 printf "\n$DEF$CON" } #------------------------{ Yes to ssh }--------------------------------------------------------------------------------- ssh_yes(){ local hostname=${hostnames["$target"]} local fprint=($(ssh-keyscan -H "$hostname" 2>/dev/null)) grep -q "${fprint[2]}" "$knwhosts" || echo "${fprint[@]}" >> "$knwhosts" } fix_id(){ local hostname=${hostnames["$target"]} local address=$(dig +short $hostname) ssh-keygen -f "$knwhosts" -R "$hostname" ssh-keygen -f "$knwhosts" -R "$address" ssh_yes } #------------------------{ System Info commands }----------------------------------------------------------------------- system_info(){ ssh $SSH_OPT $target " printf '\n${BLD}Hostname:${DEF}\n' hostname printf '\n${BLD}Interfaces:${DEF}\n' ip a printf '\n${BLD}Memory:${DEF}\n' LANG=Us free --si -h printf '\n${BLD}CPU:${DEF}\n' lscpu printf '\n${BLD}Disk:${DEF}\n' df -h; echo; df -ih; echo; lsblk printf '\n${BLD}Software:${DEF}\n' uname -a; echo [[ -e /usr/bin/lsb_release ]] && { lsb_release -a; echo; } [[ -e /usr/bin/java ]] && { java -version; echo; } [[ -e /usr/bin/psql ]] && { psql -V ; echo; } [[ -e /usr/sbin/nginx ]] && { nginx -v ; echo; } printf '${BLD}Logged in Users:${DEF}\n' who printf '\n${BLD}Port usage info:${DEF}\n' netstat -tulpn 2> /dev/null printf '\n${BLD}Processes:${DEF}\n' top -bcn1 | head -n30 " } #------------------------{ Show\Edit ssh config }----------------------------------------------------------------------- show_conf(){ clear; ssh -G $target; pause; } edit_conf(){ local new_group search=$target new_filename group_list name confs data [[ $group ]] && search="$group_id[[:space:]]*#$group#" [[ $group =~ Selected_hosts|Filtered_hosts ]] && { dialog --yesno 'Save this group as permanent config file?' 5 45 || return new_group=$(D "RUN" '' "BACK" '' --inputbox "Set new group(file) name:" 8 80 "$new_group") case $new_group:$? in '':0) return;; *:0) new_filename=~/.ssh/config_"${new_group// /_}" new_filename=${new_filename,,} [[ -f $new_filename ]] && { dialog --defaultno --yesno 'File exists, overwrite?' 5 45 || return } data="#Host DUMMY #$new_group#" group_list=("${selected_list[@]:2}") for ((i=0; i<${#group_list[@]}; i+=2)); do name="${group_list[$i]}" data+="$( echo; echo gawk -vname="$name" ' BEGIN{IGNORECASE=1} { if ($1 == "host" && $2 == name){start=1} if (start){if ($0 ~ /^#|^$/){exit}; print } } ' $CONFILES )" done echo "$data" > "$new_filename" return;; *:*) return;; esac } confs=($(grep -rilE "Host[[:space:]]*$search" $CONFILES)) || { clear; echo 'Config file not found'; pause; return; } $EDITOR "${confs[@]}" } #------------------------{ SSH to target server }----------------------------------------------------------------------- go_to_target(){ clear; ssh $SSH_OPT $target || pause; } #------------------------{ Add aliases }-------------------------------------------------------------------------------- add_aliases(){ scp $SSH_OPT ~/.bash_aliases $target:~ ssh $SSH_OPT $target "grep '. ~/.bash_aliases' .bashrc || echo '. ~/.bash_aliases' >> .bashrc" } #------------------------{ Run function on a group of servers }--------------------------------------------------------- group_run(){ local func group_list data func="$1" group_list=("${list[@]:2}") SSH_OPT_CUR="$SSH_OPT" SSH_OPT="$SSH_OPT -o ConnectTimeout=10 -o BatchMode=true" case $func in tabbed*) for ((i=0; i<${#group_list[@]}; i+=2)); do target="${group_list[$i]}" tabbed "${2/_target_/$target}" done return;; esac WAIT data=$( for ((i=0; i<${#group_list[@]}; i+=2)); do target="${group_list[$i]}" [[ $target =~ ^-+.*-+$ ]] && continue ( code="$BLD$GRN" data=$($func 2>&1 | sed ':a;N;$!ba;s/\n/\\n/g'; exit ${PIPESTATUS[0]}) || code="$BLD$RED" echo "$code----{ $target }----$DEF\\n${data:-Command did not output anything.}\\n" ) & done ) GO; printf -- '%b' "$(sort -Vk2 <<< "$data")" SSH_OPT="$SSH_OPT_CUR" } #------------------------{ Run command/script }------------------------------------------------------------------------- run_command(){ ssh $SSH_OPT $target $command; } run_script (){ scp -r $SSH_OPT "${sshto_script[2]}" $target:~/ || return 1 ssh $SSH_OPT "$target" "~/${sshto_script[1]}" } #------------------------{ Add ssh key }-------------------------------------------------------------------------------- add_sshkey(){ clear; ssh_yes > /dev/null; ssh-copy-id -i $KEY $SSH_OPT $target; } #------------------------{ Tunnelling command}-------------------------------------------------------------------------- portunneling(){ ssh $SSH_OPT $target -f -L 127.0.0.1:$LOCAL:127.0.0.1:$REMOTE sleep $TIME; } #------------------------{ Exit function }------------------------------------------------------------------------------ bye(){ printf "\n$DEF$CON" clear $LSEXIT || exit 0 lsopts='--color=auto' [[ $(uname -s) == "Darwin" ]] && lsopts='-G' ls $lsopts exit 0 }; trap bye INT #============================>-{ Dialog functions }-<=================================================================== do='--output-fd 1 --colors --no-collapse' # dialog common options eb='--extra-button' # extra hb='--help-button' # buttons cl='--cancel-label' # and el='--extra-label' # short hl='--help-label' # label ol='--ok-label' # names # Dialog buttons order and exit codes # # 0 3 1 2 D(){ # dialog creator local opts=() [[ $1 ]] && opts+=("$ol" "$1") [[ $2 ]] && opts+=("$el" "$2" "$eb") [[ $3 ]] && opts+=("$cl" "$3") [[ $4 ]] && opts+=("$hl" "$4" "$hb") shift 4 dialog "${opts[@]}" $do "$@" } #------------------------{ Change alternative username }---------------------------------------------------------------- username(){ new_user=$(D "CHANGE" '' "BACK" '' --max-input 20 --inputbox 'Change alternative username' 10 30 $GUEST) case $new_user:$? in *:0) GUEST=${new_user:-$GUEST}; SSH_OPT="-oUser=$GUEST"; USERNOTE="Username changed to \Z2$GUEST\Z0.";; *:*) return;; esac } #------------------------{ Create custom command/script }--------------------------------------------------------------- custom(){ local runner= [[ $group ]] && runner='group_run' new_command=$(D "RUN" '' "BACK" '' --inputbox "Write down your command here:" 8 120 "$new_command") case $new_command:$? in '':0) custom;; *:0) command=$new_command; clear; $runner run_command; pause;; *:*) return;; esac } script(){ [[ -f ${sshto_script[2]} ]] || { echo -e '#!/bin/bash\necho "Running sshto script"' > "${sshto_script[2]}" chmod +x "${sshto_script[2]}" } script_text=$(cat "${sshto_script[2]}") D "RUN" "EDIT" '' "BACK" --msgbox "$script_text" 40 120 case $? in 0) [[ $script_text ]] || script; clear; $runner run_script; pause;; 3) $EDITOR "${sshto_script[2]}"; script;; 2) second_dialog;; esac } #------------------------{ Change local port for tunnelling }----------------------------------------------------------- local_port(){ new_local=$(D "CHANGE" '' "BACK" '' --max-input 5 --inputbox 'Change local port' 10 30 $LOCAL) LOCAL=${new_local:-$LOCAL} } #------------------------{ Change remote port for tunnelling }---------------------------------------------------------- remote_port(){ new_remote=$(D "CHANGE" '' "BACK" '' --max-input 5 --inputbox 'Change remote port' 10 30 $REMOTE) REMOTE=${new_remote:-$REMOTE} } #------------------------{ Upload\Download and mount dialogs }---------------------------------------------------------- downpath(){ new_path=$(D "CHANGE" '' "BACK" '' --max-input 100 --inputbox 'Change download folder' 10 50 $DEST) DEST=${new_path:-$DEST} dfilelist= } homepath(){ new_path=$(D "CHANGE" '' "BACK" '' --max-input 100 --inputbox 'Change home folder' 10 50 $home) home=${new_path:-$home} } uploader(){ printf "Uploading $BLD$ufilename$DEF\n" rsync $rsyncopt --rsh="ssh $SSH_OPT" $ufilename $target:"$DEST/" } mountdest(){ which sshfs &> /dev/null || { clear; how_to_install sshfs; pause; return; } clear; sshfs $sshfsopt "$target":"$DEST" "$home" || pause } unmountdest(){ mount | grep -q "$home" && umount "$home"; } copy_files(){ local runner= [[ $group ]] && runner='group_run' ufilename=$(D "COPY" '' "BACK" '' --fselect $PWD/ 10 80) case $ufilename:$? in $PWD|$PWD/:0) return;; *:0) clear; $runner uploader; pause;; *:*) return;; esac ; copy_files } upload(){ local runner= [[ $group ]] && runner='group_run' ufilelist=( $(ls -sh1 $home | awk '{print $2,$1}') ) ufilename=$(D "UPLOAD" '' "BACK" '' --menu "Select file\folder to upload:" 0 0 0 "${ufilelist[@]:2}") case $? in 0) [[ $ufilename ]] || upload clear; $runner uploader; pause;; *) return;; esac; upload } download(){ [[ $dfilelist ]] || { WAIT dfilelist=$(ssh $SSH_OPT $target ls -sh1 $DEST 2>&1) \ && dfilelist=( $(awk '{print $2,$1}' <<< "$dfilelist") ) \ || { clear echo "$dfilelist" pause dfilelist= second_dialog } GO } dfilename=$(D "DOWNLOAD" '' "BACK" '' --menu "Select file\folder to download:" 0 0 0 "${dfilelist[@]:2}") case $? in 0) [[ $dfilename ]] || download clear printf "Downloading $BLD$dfilename$DEF\n" rsync $rsyncopt --rsh="ssh $SSH_OPT" $target:"$DEST/$dfilename" . || pause;; *) return;; esac; download } #------------------------{ Switch menu mode to contents view or full list }--------------------------------------------- save_tmp(){ echo "$1" > "$tmpfile"; chmod 600 "$tmpfile"; } new_list(){ list=(); match= for item in "${selected_list[@]}" "${filtered_list[@]}" "${fullist[@]}"; { case $item:$match in *{\ *\ }*:1) break ;; *{\ $filter\ }*:*) match=1;; esac [[ $match ]] && list+=( "$item" ) } [[ $filter =~ Selected ]] && return [[ ${list[*]} ]] && save_tmp "filter='$filter'" || { list=( "${fullist[@]}" ); rm "$tmpfile"; } } contents_menu(){ local filter_tmp=$filter selected= filtered= [[ ${selected_list[@]} ]] && selected='Selected_hosts' [[ ${filtered_list[@]} ]] && filtered='Filtered_hosts' local btns=('SELECT' 'RUN COMMAND' 'RELOAD' 'BACK') filter=$(D "${btns[@]}" --no-items --menu "Select list of hosts:" 0 0 0 "All" $selected $filtered "${content[@]}" '' "$qcmd" "$qbut1") case $filter:$? in "":[03]) contents_menu;; "$qcmd":[03]) contents_menu;; "$qbut1":[03]) filter_items 'first_dialog';; All:0) list=( "${selected_list[@]}" "${fullist[@]}" ) save_tmp "filter=";; *:3) second_dialog "$filter" ;; *:2) filter=$filter_tmp;; *:1) restart; contents_menu;; *:0) new_list;; esac ; first_dialog } # Dialog buttons order and exit codes # # 0 3 1 2 #------------------------{ Selector }----------------------------------------------------------------------------------- declare -A slctd_hosts declare -A slctd_groups gen_selected_list(){ local k selected_list=('-----------{ Selected_hosts }-----------' '_LINE_') for k in "${!slctd_hosts[@]}"; { selected_list+=("$k" "${slctd_hosts[$k]}"); } } slct_dslct(){ local desc k v # remove from selection [[ ${slctd_hosts[$target]} && $1 != "select" ]] && { unset slctd_hosts[$target] gen_selected_list ((${#selected_list[@]}==2)) && unset selected_list return } [[ $1 == "deselect" ]] && return # add to selection for ((k=0,v=1; kdesclength)) && desclength=${#desc} hostnames["$name"]=$hostname #Create host:hostname pairs in hostnames array fullist+=("$name" "$desc") #Add Host and Description to the list done < <(gawk ' BEGIN{IGNORECASE=1} /Host /{ strt=1 host=$2 desc=gensub(/^.*Host .* #(.*)/, "\\1", "g", $0) desc=gensub(/(.*)#.*/, "\\1", "g", desc) next } strt && host == "'"$group_id"'"{ print "group_name", "dummy", desc strt=0 } strt && /HostName /{ hostname=$2 print host, hostname, desc strt=0 }' $CONFILES) descline=$(line - $desclength) list=( "${fullist[@]}" "${quick_butt[@]}" ) N=${#fullist[@]} qcmd='-----------{ Quick commands }-----------' qbut1='1 Filter items' quick_butt=( '' '' "$qcmd" "$descline" "$qbut1" "Create list of hosts filtered by pattern" ) } #--{ Go baby, GO!) }-- check_confile; restart [[ -e $tmpfile ]] && . "$tmpfile" [[ $filter ]] && new_list [[ $target ]] || first_dialog [[ $OPT =~ name ]] && { echo $target; exit; } [[ $target ]] && second_dialog bye #:)