--- /dev/null
+#!/bin/bash
+
+#########################################################################
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the version 3 of the GNU General Public License #
+# as published by the Free Software Foundation. #
+# #
+# This program is distributed in the hope that it will be useful, but #
+# WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
+# General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+# Written by and Copyright (C) Francois Fleuret #
+# Contact <francois@fleuret.org> for comments & bug reports #
+#########################################################################
+
+set -e
+set -o pipefail
+
+######################################################################
+
+function check_remote_is_defined () {
+ if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
+ then
+ return 0
+ else
+ echo "@XREMOTE_HOST should come first." >&2
+ exit 1
+ fi
+}
+
+function help () {
+ cat <<EOF
+xremote.sh <executable>
+
+ This script takes a script as argument and executes it remotely in a
+ temporary directory on a ssh-accessible server.
+
+ It parses the script first to find embedded arguments which defines
+ the hostname on which to run it, the files to send, the files to
+ get back when the execution is done, and commands to execute before
+ running the executable remotely.
+
+ These arguments can appear multiple times, except the one that
+ specifies the remote host.
+
+ Example:
+
+ @XREMOTE_HOST: elk.fleuret.org
+ @XREMOTE_SEND: mnist.py
+ @XREMOTE_GET: *.dat
+ @XREMOTE_PRE: ln -s /home/fleuret/data/pytorch ./data
+
+ Contact <francois@fleuret.org> for comments.
+
+EOF
+ return 0
+}
+
+function cleanup_remote_tmp () {
+ if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
+ then
+ echo "Clean up remote workdir."
+ ssh "${REMOTE_HOST}" "rm -rf \"${REMOTE_DIR}\""
+ fi
+}
+
+######################################################################
+
+[[ -x "$1" ]] || (help && exit 1)
+
+main="$(basename "$1")"
+
+cd "$(dirname "$1")"
+
+trap cleanup_remote_tmp EXIT
+
+######################################################################
+
+while read line
+do
+
+ if [[ "${line}" =~ '@XREMOTE' ]]
+ then
+
+ label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
+ value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
+
+ case "${label}" in
+
+ PRE)
+ check_remote_is_defined
+ ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
+ ;;
+
+ SEND)
+ check_remote_is_defined
+ tar c "${value}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
+ ;;
+
+ HOST)
+ [[ "${REMOTE_DIR}" ]] && (exit "Remote host already defined!" >&2 && exit 1)
+ cleanup_remote_tmp
+ REMOTE_HOST=${value}
+ REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote.from_"$(hostname)_$(date +%Y%m%d)".XXXXXX)"
+ ;;
+ esac
+ fi
+
+done < "${main}"
+
+######################################################################
+
+check_remote_is_defined
+
+tar c "${main}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
+
+echo "----------------------------------------------------------------------"
+echo "-- On ${REMOTE_HOST}"
+echo "----------------------------------------------------------------------"
+ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ./$(basename "${main}")"
+echo "----------------------------------------------------------------------"
+
+######################################################################
+
+# Disable globbing to keep wildcards for the remote side
+
+set -f
+
+while read line
+do
+ if [[ "${line}" =~ '@XREMOTE' ]]
+ then
+ label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
+ value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
+ case "${label}" in
+ GET)
+ check_remote_is_defined
+ ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar c ${value}" | tar mxv
+ ;;
+ esac
+ fi
+done < "${main}"
+
+set +f
+
+######################################################################