'iTerm2: How can I trigger a local command from a remote session?
iTerm2 shell integration has some neat tricks, such as its it2copy
command, which copies into the local clipboard, even if I'm logged into a remote machine via ssh.
Can it be used to run arbitrary shell commands?
For instance, while I'm logged in over ssh, I want to execute a command to open an editor on my local machine. VSCode can open a remote directory with this command:
code --remote ssh-remote+myserver /home/stuart/some-directory
I want to trigger that command locally, from an ssh session on the remote machine.
PS -- I know there's an alternative: Create a (nested) ssh connection back to my local machine to execute the commands over ssh, rather than using iTerm2's backchannel. But that has various downsides, hence this question.
I'm also aware of the PermitLocalCommand
option in ~/.ssh/config
, which allows me to send an escape code (~C
), followed by a local command (!code --remote ...
). But I'm hoping for a solution I can use in a script or bash alias.
For instance, if it2local
existed, I would use it like this:
alias code_here='it2local "code --remote ssh-remote+$(uname -n) $(pwd)"'
If that is possible with ssh alone, I'd love to hear about it.
Solution 1:[1]
The proper way to do this is via iTerm2 Triggers, which can run an arbitrary command (among other options) whenever a specific pattern appears in your terminal output.
The hypothetical it2local
command I described above would just have to echo some predefined trigger pattern to your terminal, along with the command you want to execute.
In my case, I didn't implement the generic it2local
command. (Maybe I'll update this answer later.) For now, I've implemented a script that serves my specific use-case: Opening a remote file with VSCode. The code I'm using is shown below.
#!/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
# [email protected]
# 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 "$@"
Solution 2:[2]
For anyone who still wants to do this:
My solution is inspired by @StuartBerg. THX a lot.
- In iTerm2, find triggers:
iTerm2 -> Perenfences -> Profiles -> Advanced -> Triggers -> Edit
and set the triggers as follows(remember to restart the iTerm2 after set):
# open a folder
- Regular Expression: ^open this folder in local vscode\(s\):(.+)
- Action: Run Command...
- Parameters: /path/to/your/vscode --folder-uri=vscode-remote://ssh-remote+${your remote ip}\1
# open a file
- Regular Expression: ^open this file in local vscode\(s\):(.+)
- Action: Run Command...
- Parameters: /path/to/your/vscode --file-uri=vscode-remote://ssh-remote+${your remote ip}\1
- Set the command alias in your remote server, usually in
~/.bashrc
for linux(most behaviors of thecode
alias are the same as those in local terminal, except that any file that does not exit in the server will not be opened):
# pay attention to " and '
alias code='func(){
if [[ "$1" == "." ]]; then
CODE_PATH=$(pwd | tr -d "\n\r");
else
if [[ "$1" == /* ]]; then
CODE_PATH="$1";
else
CODE_PATH=$(pwd | tr -d "\n\r")/"$1";
fi;
fi;
if [[ -d $CODE_PATH ]]; then
echo open this folder in local vscode"("s")":$CODE_PATH;
else
if [[ -f $CODE_PATH ]]; then
echo open this file in local vscode"("s")":$CODE_PATH;
else
echo No such file or directory: $CODE_PATH;
fi;
fi;
};func'
- make the alias take effect:
$ source ${path/to/your/alias/file}
- In the ssh terminal,
cd
to the directory you want to open in local vscode (or use absolute pathes start with/
) and do:
# open the current folder
$ code .
# output:
# open this folder in local vscode(s):/folder/you/want/to/open
# open a file or a folder in current directory
$ code ${filename or folder}
# output:
# open this file(folder) in local vscode(s):/path/you/want/to/open
# open an absolute path, i.e, any path that starts with `/`
$ code `/${absolute/path}`
If you've set the ssh key to the remote server locally, then the directory /path/you/want/to/open
will be opened in your local vscode.
PS:
^
?\(s\)
and '('s')'
are added on purpose as to make a little difference between the config file and the trigger Regular Expression
so that the trigger won't fired accidentally when you open the alias config file. You can modify it any way you like.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |