首页 > 解决方案 > iTerm2:如何从远程会话触发本地命令?

问题描述

iTerm2 shell 集成有一些巧妙的技巧,例如它的it2copy命令,即使我通过 ssh 登录到远程机器,它也会复制到本地剪贴板。

它可以用来运行任意shell命令吗?

例如,当我通过 ssh 登录时,我想执行一个命令以在我的本地计算机上打开一个编辑器。VSCode 可以使用以下命令打开远程目录:

code --remote ssh-remote+myserver /home/stuart/some-directory

我想从远程机器上的 ssh 会话本地触发该命令。


PS——我知道有一个替代方案:创建一个(嵌套的)ssh 连接回到我的本地机器以通过 ssh 执行命令,而不是使用 iTerm2 的反向通道。但这有各种缺点,因此有这个问题。

我也知道 中的PermitLocalCommand选项~/.ssh/config,它允许我发送转义码 ( ~C),后跟本地命令 ( !code --remote ...)。但我希望有一个可以在脚本或 bash 别名中使用的解决方案。

例如,如果it2local存在,我会这样使用它:

alias code_here='it2local "code --remote ssh-remote+$(uname -n) $(pwd)"'

如果单独使用 ssh 可以做到这一点,我很想听听。

标签: sshiterm2

解决方案


执行此操作的正确方法是通过iTerm2 Triggers,只要终端输出中出现特定模式,它就可以运行任意命令(以及其他选项)。

我上面描述的假设it2local命令只需将一些预定义的触发模式与您要执行的命令一起回显到您的终端。

就我而言,我没有实现通用it2local命令。(也许我稍后会更新这个答案。)现在,我已经实现了一个服务于我的特定用例的脚本:使用 VSCode 打开一个远程文件。我正在使用的代码如下所示。

#!/bin/sh

#
# This file contains the code and instructions to set up an iTerm2 "Trigger" from a
# remote ssh session that will open up VSCode on your local machine to edit a
# file on the remote server over ssh.
#
# Author: Stuart Berg
#         https://github.com/stuarteberg
#         bergs@janelia.hhmi.org
#         https://stackoverflow.com/questions/61699447

# SETUP OVERVIEW
# --------------
# - Install the VS Code Remote Development Extension Pack
# - Ideally, setup passwordless ssh access to the remote machines you want to access
# - Place this script somewhere on your local machine (and make sure it's executable).
# - Copy the localcode() shell function below into your remote machine's .bashrc
# - Define the Trigger in iTerm2 as defined below.
#
# Notes:
#   Docs for iTerm2 Triggers: https://iterm2.com/documentation-triggers.html
#   Docs for VSCode Remote Extension: https://code.visualstudio.com/docs/remote/remote-overview
#   - CLI: https://github.com/microsoft/vscode-remote-release/issues/585#issuecomment-536580102

# iTerm2 Preferences Setup
# ------------------------
#
# In your iTerm2 preferences, set up a Trigger (Profiles > Advanced > Triggers > Edit)
#
# Regular Expression:  .*ITERM-TRIGGER-open-with-local-vscode-remote ([^ ]+) ([^ ]+) (([^ ]+ ?)+)
#             Action:  Run Command...
#         Parameters:  /path/to/this/script \1
#
# Tip: For additional feedback, try adding a duplicate entry with a "Post Notifcation" action.

# HOW TO TEST
# -----------
#
# NOTE: The new trigger will not be active for already-open terminal sessions.
#       Open a new terminal after you add the trigger to your preferences.
#
# To test it, ssh into the remote machine, and try the 'localcode' function:
#
#   localcode .
#   localcode /some/dir
#   localcode /some/file
#   localcode /some/file remote-machine-name
#
# If something is going wrong, inspect /tmp/iterm-vscode-trigger.log

#
# Put this in your remote ~/.bashrc
#
localcode() (
    # Tell zsh to use bash-style arrays
    setopt ksh_arrays 2> /dev/null || true

    CMD=ITERM-TRIGGER-open-with-local-vscode-remote
    MACHINE=${LOCALCODE_MACHINE-submit}
    FILENAMES=( "$@" )

    if [[ ${#FILENAMES[@]} == 0 ]]; then
        FILENAMES=.
    fi

    if [[ ${#FILENAMES[@]} == 1 && -d ${FILENAMES[0]} ]]; then
            FILENAMES[0]=$(cd ${FILENAMES[0]}; pwd)
            FTYPE=directory
    else
        # Convert filenames to abspaths
        for (( i=0; i < ${#FILENAMES[@]}; i++ )); do
            FN=${FILENAMES[i]}
            if [[ -f ${FN} ]]; then
                DIRNAME=$(cd $(dirname ${FN}); pwd)
                FILENAMES[i]=${DIRNAME}/$(basename ${FN})
                FTYPE=file
            else
                1>&2 echo "Not a valid file: ${FN}"
                exit 1
            fi
        done
    fi

    echo ${CMD} ${FTYPE} ${MACHINE} ${FILENAMES[@]}
)
export -f localcode

#
# Copy this whole file onto your local machine, or at least the following lines.
# Make sure it is executable (chmod +x /path/to/this/script)
#
trigger_vscode_remote_editing() (
    # Tell zsh to use bash-style arrays
    setopt ksh_arrays 2> /dev/null || true

    # The git extension runs 'git status -z -u' on the remote machine,
    # which takes a very long time if the remote directory is a git repo
    # with a lot of untracked files.
    # That can be fixed if you configure .gitignore appropriately,
    # but for my purposes it's easier to just disable git support when editing remote files.
    # If you want git support when using remote SSH, then comment out this line.
    # See: https://github.com/microsoft/vscode-remote-release/issues/4073
    VSCODE='/usr/local/bin/code'
    VSCODE="${VSCODE} --disable-extension vscode.git --disable-extension vscode.github --disable-extension waderyan.gitblame"
    LOGFILE=/tmp/iterm-vscode-trigger.log
    FTYPE=$1
    MACHINE=$2
    FILEPATHS=( "$@" )
    FILEPATHS=( "${FILEPATHS[@]:2}" )

    TS="["$(date "+%Y-%m-%d %H:%M:%S")"]"
    echo "${TS} Triggered: ""$@" >> ${LOGFILE}

    # https://github.com/microsoft/vscode-remote-release/issues/585#issuecomment-536580102
    if [[ "${FTYPE}" == "directory" ]]; then
        CMD="${VSCODE} --remote ssh-remote+${MACHINE} ${FILEPATHS[@]}"
        echo "${TS} ${CMD}" >> ${LOGFILE}
        ${CMD}
    elif [[ "${FTYPE}" == "file" ]]; then
        for FN in ${FILEPATHS[@]}; do
            CMD="${VSCODE} --file-uri vscode-remote://ssh-remote+${MACHINE}${FN}"
            echo "${TS} ${CMD}" >> ${LOGFILE}
            ${CMD}
        done
    else
        echo "${TS} Error: Bad arguments." >> ${LOGFILE}
        exit 1
    fi
)

trigger_vscode_remote_editing "$@"

推荐阅读