remote_open: ssh終端下一鍵用本地軟件開遠(yuǎn)程文件(夾)

引子

使用ssh連接服務(wù)器,不可避免會(huì)有用本地的軟件開遠(yuǎn)程文件的需求,比如查看遠(yuǎn)程圖片,或用本地文本編輯器打開遠(yuǎn)程文件。

顯然,此時(shí)得將遠(yuǎn)程文件傳達(dá)本地,手段不一而足,scp、sftp、rsycn、sshfs等等。然而,不論哪一種,都需要開著兩個(gè)窗口,一個(gè)是ssh連接服務(wù)器的終端,一個(gè)是用來敲傳文件命令的終端。更麻煩的是,得對(duì)著前者的服務(wù)器ip、用戶名、文件路徑,來敲后者的命令。要知道,在本地,我們只需要輸入open <file_path(s) or dir_path(s)>就能一鍵打開文件(夾)。

為了免去上述體力勞動(dòng),我實(shí)現(xiàn)了remote open工具,可以像在本地用open命令一樣,一鍵打開遠(yuǎn)程文件(夾)。

源代碼:https://github.com/hyliang96/remote_open.git

介紹

功能

ssh連到服務(wù)器后,對(duì)著遠(yuǎn)程的終端:

  • 輸入opennc <file_path(s) or dir_path(s)>,根據(jù)文件后綴本,自動(dòng)選用地軟件,打開遠(yuǎn)程的文件(夾)
  • 輸入 subnc <file_path(s) or dir_path(s)>,用本地sublime,自動(dòng)打開遠(yuǎn)程的文件(夾)。(你也可以修改代碼,換成你喜歡的文本編輯器)

本腳本自動(dòng)用sshfs掛載遠(yuǎn)程目錄,無需手動(dòng)同步文件

依賴

  • 服務(wù)器:nc
  • 本地:nc、ssh、sshfs、zsh

原理

  • ssh連接服務(wù)器,并用-R參數(shù)進(jìn)行端口轉(zhuǎn)發(fā),以供nc使用
  • 在遠(yuǎn)程輸入opennc <file_path(s) or dir_path(s)>
  • 遠(yuǎn)程將 [ 【hostname】、【username】、<file_path(s) or dir_path(s)>之絕對(duì)路徑],由nc發(fā)送到本地
  • 本地偵聽到收到遠(yuǎn)程nc所發(fā),按照其要求將<username>@< hostname>:/sshfs掛載到本地
  • 用本地的open打開所目標(biāo)文件(夾)

實(shí)現(xiàn)

遠(yuǎn)程

目的:遠(yuǎn)程將 [ 【hostname】、【username】、<file_path(s) or dir_path(s)>之絕對(duì)路徑],由nc發(fā)送到本地

在~/.bashrc (或~/.bashrc 會(huì)加載的.bash_aliases、.bash_function)中,添加以下代碼。

subnc(){
    dirs=""
    for arg in $@; do
        if [ -e $arg ]; then
            path_arg=$(abspath $arg)
            dirs="$dirs; $path_arg"
        else
            echo not exist: $arg
        fi
    done
    if [ "${dirs:0:2}" = "; " ]; then
        dirs=${dirs:2}
    fi
    if [ "$dirs" != "" ]; then
        (echo "sub; $USER; `hostname`; $dirs" | nc localhost 【遠(yuǎn)程nc端口】  & )   > /dev/null
    fi
}

opennc(){
    dirs=""
    for arg in $@; do
        if [ -e $arg ]; then
            path_arg=$(abspath $arg)
            dirs="$dirs; $path_arg"
        else
            echo not exist: $arg
        fi
    done
    if [ "${dirs:0:2}" = "; " ]; then
        dirs=${dirs:2}
    fi
    if [ "$dirs" != "" ]; then
        (echo "open; $USER; `hostname`; $dirs" | nc localhost 【遠(yuǎn)程nc端口】  & )   > /dev/null
    fi
}

本地

對(duì)sshfs的封裝 -- easy_sshfs

創(chuàng)建 ~/remote_open/easy_sshfs.sh

#!/bin/bash

mount_dir=【自己設(shè)置一個(gè)文件夾,其下專門用來sshfs掛載遠(yuǎn)程目錄】
# --------------------- sshfs --------------------------
# 掛載一個(gè)磁盤
fs() # fs host別名 [遠(yuǎn)端路徑, 默認(rèn)為"."]
{
    if [ "$#" -lt "1" ]; then
        echo "Usage: fs host別名 [遠(yuǎn)端路徑, 默認(rèn)為'.']"
        return
    fi
    host=$1
    if [ "$#" -gt "1" ]; then
        remotepath="$2"
    else
        remotepath="."
    fi
    localpath="$mountdir/${host}"

    if [ "`pgrep -lf sshfs | grep \"$localpath \"`" != "" ]; then
        echo $localpath is already mounted, now remount
        umount $localpath # 若在mount此localpath,則關(guān)閉之
    else
        umount $localpath >/dev/null 2>&1 # 不輸出,但任然驗(yàn)證一遍
    fi

    if [ ! -d $localpath ]; then mkdir $localpath; fi # 若無掛載目錄文件夾,則創(chuàng)建之

    sshfs $host:$remotepath $localpath -o volname=$host -o reconnect -o transform_symlinks -o follow_symlinks
    #  -o local
}
# 卸掛載一個(gè)磁盤
ufs()  # ufs [[用戶名@]host別名 | 用戶名@網(wǎng)址 ] (一個(gè)或多個(gè))
{
    if [ "$#" -lt "1" ]; then
        echo "Usage: fs [[用戶名@]host別名 | 用戶名@網(wǎng)址 ] (一個(gè)或多個(gè))"
        return
    fi

    for host in $@; do
        # host=$1
        localpath="$mountdir/${host}"

        umount $localpath
        if [ "`ls -A $localpath`" = "" ]; then
            rm $localpath -rf # 若掛載目錄是空文件夾,則刪除之
        else
            echo original dir $localpath is now recovered
        fi
    done
}
# 列出目前mount的所有磁盤
fsls()
{
    answer=`pgrep -lf sshfs | awk '{print $1 "  " $3}'`
    if [ "$answer" != "" ]; then echo "pid   host_alias:remote_path"; echo $answer; fi
}
# 列出掛載路徑
mtls()
{
    ls $mountdir -la
}

自動(dòng)掛載遠(yuǎn)程目錄并打開文件

創(chuàng)建 ~/remote_open/remote_open.sh

#!/bin/bash
. ~/remote_open/easy_sshfs.sh
debug=0     # 需要debug輸出設(shè)為1

string="$1"

args=("${(@s/; /)string}")
[ $debug -ne 0 ] && declare -p args

app=${args[1]}
user=${args[2]}
host_alias=${args[3]}
remote_paths=(${args[@]:3:${#args[@]}})

[ $debug -ne 0 ] && echo app: $app
[ $debug -ne 0 ] && echo user: $user
[ $debug -ne 0 ] && echo host_alias: $host_alias
[ $debug -ne 0 ] && declare -p remote_paths


mounts="$(pgrep -lf sshfs | awk '{print $3}')"
mounts=("${(@s/\\n/)mounts}")
[ $debug -ne 0 ] && echo mounts: $mounts

mount_dir=$mountdir
[ $debug -ne 0 ] && echo mount_dir: $mount_dir

mount_folder=$user@$host_alias

if ! [[ ${mounts} =~ $mount_folder:/ ]] || ! [ -d $mount_dir/$host_alias/home ]; then
    [ $debug -ne 0 ] && echo hasnt mounted $mount_folder:/, start mounting
    [ $debug -ne 0 ] && echo fs $mount_folder /
    fs $mount_folder /
    [ $debug -ne 0 ] && echo finished mounting
else
    [ $debug -ne 0 ] && echo has mounted $mount_folder:/
fi


folders=()
files=()

for remote_path in ${remote_paths}; do
    [ $debug -ne 0 ] && echo remote_path: $remote_path
    local_path=$mount_dir/$mount_folder/$remote_path
    [ $debug -ne 0 ] && echo local_path: $local_path

    if [ -d $local_path ]; then
        [ $debug -ne 0 ] && echo local_path is dir
        folders+="$local_path"
    elif [ -f $local_path ]; then
        [ $debug -ne 0 ] && echo local_path is file
        files+="$local_path"
    else
        echo no such file or directory: $local_path
    fi
done

[ $debug -ne 0 ] && declare -p folders
[ $debug -ne 0 ] && declare -p files

# 用本地軟件打開,適用于mac,windows用戶需修改下面代碼
if [ "$app" = "sub" ]; then
    # 你也可以修改代碼,換成你喜歡的文本編輯器
    open_with="/Applications/Sublime Text.app"
elif [ "$app" = "open" ]; then
    open_with=""
fi

if [ "$open_with" = "" ]; then
    for folder in $folders; do
        # folder=${folder/ /\\ }
        [ $debug -ne 0 ] && echo $folder
        open  $folder
    done
    if [ $#files -ne 0 ]; then
        open $files
    fi
else
    for folder in $folders; do
        # folder=${folder/ /\\ }
        [ $debug -ne 0 ] && echo $folder
        open -a $open_with $folder
    done
    if [ $#files -ne 0 ]; then
        open -a $open_with $files
    fi
fi

端口監(jiān)聽

編輯 ~/remote_open/remote_open_listen.sh

#!/bin/zsh

port=【本地nc端口】

this_dir_abs_path=$(cd "$(dirname "$0")"; pwd)

echo start nc
while read line; do
    echo "$line"
    zsh $this_dir_abs_path/remote_open.sh $line
done < <(nc -lk $port)
echo end nc

設(shè)置.ssh/config,進(jìn)行端口轉(zhuǎn)發(fā)

為使服務(wù)器上nc發(fā)送消息到本地被監(jiān)聽到,ssh需將【遠(yuǎn)程nc端口】同【本地nc端口】轉(zhuǎn)發(fā)

Host 【hostname】
    HostName 【服務(wù)器的外網(wǎng)ip 或 url】
    User 【username】
    # 私鑰
    IdentityFile ~/.ssh/id_rsa
    PreferredAuthentications publickey
    # 用于remote open 的端口轉(zhuǎn)發(fā)
    RemoteForward 【遠(yuǎn)程nc端口】 localhost:【本地nc端口】
   # 其他設(shè)置...

其中

  • 【hostname】=服務(wù)器上執(zhí)行hostname的輸出
  • 【username】=服務(wù)器上執(zhí)行echo $USER的輸出
  • 【服務(wù)器的外網(wǎng)ip 或 url】=服務(wù)器上執(zhí)行curl ifconfig.me的輸出

設(shè)端口監(jiān)聽腳本為開機(jī)自啟

將端口監(jiān)聽腳本~/remote_open/remote_open_listen.sh設(shè)置為開機(jī)自啟、后臺(tái)運(yùn)行

mac用戶

操作教程詳見

  • 在 “自動(dòng)操作.app”中新建“應(yīng)用程序”,
  • 在其中選中運(yùn)行shell腳本,
  • 選擇使用zsh
  • 自動(dòng)操作腳本內(nèi)容寫
( ( 
zsh ~/remote_open/remote_open_listen.sh
) &) > /dev/null 2>&1
  • 保存到 ~/remote_open/remote_open.app

  • 系統(tǒng)偏好設(shè)置/用戶與群組/登錄項(xiàng),將~/remote_open/remote_open.app選中,并勾選隱藏,設(shè)為開機(jī)啟動(dòng)項(xiàng)目。

windows用戶

請(qǐng)自行查找windows設(shè)置開機(jī)自動(dòng)運(yùn)行腳本的方法


本人原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容