#########################################################################
set -e
-set -o pipefail
+
+# set -o pipefail
######################################################################
function help () {
cat <<EOF
-xremote.sh <executable>
+xremote.sh [--help] [-h <remote_host>] [-d <remote_dir>] [-r <local_result_dir>] [-i] <script> [script arguments]
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
+ It parses the script first to find embedded arguments which define
+ the hostname on which to run, the files to send, the files to get
+ back when the execution is over, and commands to execute before
running the executable remotely.
These arguments can appear multiple times, except the one that
Example:
@XREMOTE_HOST: elk.fleuret.org
- @XREMOTE_SEND: mnist.py
+ @XREMOTE_EXEC: python3
+ @XREMOTE_SEND: main.cf
@XREMOTE_GET: *.dat
@XREMOTE_PRE: ln -s /home/fleuret/data/pytorch ./data
- Contact <francois@fleuret.org> for comments.
+ If a file with the same name as the script with the .xremote
+ extension appended to it exists, arguments will be read from it by
+ default.
+
+ If the -h option is provided @XREMOTE_HOST is ignored.
+
+ If the -d option is provided, the provided directory is used and
+ kept, instead of a temporary one
+
+ If the -i option is provided, all the files are installed and
+ scripts run in the specified directory on the remote host, but the
+ main executable and post-run commands are ignored
+
+ If the -r option is provided, the result files specified with
+ @XREMOTE_GET will be downloaded there instead of the current
+ directory.
+
+ If no argument is provided to @XREMOTE_HOST, and the -h option is
+ not specified, the environment variable \$XREMOTE_HOST is used
+ instead
+
+ 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}\""
+ if [[ "${ARG_DIR}" ]]
+ then
+ echo "xremote: Keeping remote workdir."
+ else
+ echo "xremote: Cleaning up temporary remote workdir."
+ ssh "${REMOTE_HOST}" rm -rf "${REMOTE_DIR}"
+ fi
fi
}
######################################################################
-[[ -x "$1" ]] || (help && exit 1)
+while [[ "$1" =~ ^- ]]
+do
+ case "$1"
+ in
+ -h)
+ shift
+ ARG_HOST="$1"
+ [[ ${ARG_HOST} ]] || (echo "xremote: Hostname missing." && exit 1)
+ echo "xremote: remote forced to ${ARG_HOST}"
+ ;;
+
+ -d)
+ shift
+ ARG_DIR="$1"
+ [[ ${ARG_DIR} ]] || (echo "xremote: Directory missing." && exit 1)
+ echo "xremote: remote dir set to ${ARG_DIR}"
+ ;;
+
+ -i)
+ NO_RUN=1
+ echo "xremote: no run"
+ ;;
+
+ -r)
+ shift
+ ARG_RESULT_DIR="$1"
+ [[ ${ARG_RESULT_DIR} ]] || (echo "xremote: Directory missing." && exit 1)
+ echo "xremote: result dir set to ${ARG_RESULT_DIR}"
+ ;;
+
+ --help)
+ help
+ exit 0
+ ;;
+
+ *)
+ echo "xremote: Unknown option $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
-main="$(basename "$1")"
+######################################################################
+
+[[ "$1" ]] || (echo "xremote: Script name missing" && exit 1)
+
+[[ -a "$1" ]] || (help && echo >&2 "xremote: Cannot find script \`$1'" && exit 1)
cd "$(dirname "$1")"
+main="$(basename "$1")"
+main_config="${main}.xremote"
+
+if [[ -f "${main_config}" ]]
+then
+ echo "xremote: found ${main_config}"
+else
+ main_config="${main}"
+fi
+
+shift
+
trap cleanup_remote_tmp EXIT
######################################################################
if [[ "${line}" =~ '@XREMOTE' ]]
then
- label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
- value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
+ label=$(sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/' <<<"${line}")
+ value=$(sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/' <<<"${line}")
case "${label}" in
+ EXEC)
+ check_remote_is_defined
+ [[ "${REMOTE_EXEC}" ]] && (exit "Remote executable already defined!" >&2 && exit 1)
+ REMOTE_EXEC="${value}"
+ ;;
+
PRE)
check_remote_is_defined
+ echo "xremote: ${value}"
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"
+ echo "xremote: -- sending files --------------------------------------------"
+ tar ch ${value} | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mxv"
;;
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)"
+ REMOTE_HOST="${ARG_HOST}" # Host given in argument has priority
+ [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${value}"
+ [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${XREMOTE_HOST}"
+ [[ "${REMOTE_HOST}" ]] || (echo "xremote: No remote host specified." >&2 && exit 1)
+ if [[ "${ARG_DIR}" ]]
+ then
+ ssh </dev/null "${REMOTE_HOST}" "mkdir -p \"${ARG_DIR}\""
+ REMOTE_DIR="${ARG_DIR}"
+ else
+ REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote_\$\(whoami\)_from_"$(hostname)_$(date +%Y%m%d_%H%M%S)".XXXXXX)"
+ fi
+ echo "xremote: target is ${REMOTE_HOST}"
;;
esac
fi
-done < "${main}"
+done < "${main_config}"
######################################################################
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 "----------------------------------------------------------------------"
+if [[ "${NO_RUN}" ]]
+then
+ echo "xremote: everything has been set up in ${REMOTE_HOST}:${ARG_DIR}"
+ exit 0
+fi
+
+echo "xremote: -- running the executable -----------------------------------"
+
+if [[ "${REMOTE_EXEC}" ]]
+then
+ REMOTE_COMMAND="${REMOTE_EXEC} ${main}"
+else
+ REMOTE_COMMAND="./${main}"
+fi
+
+######################################################################
+
+# I find this slightly ugly ...
+
+for s in "$@"
+do
+ quoted_args="${quoted_args} \"${s}\""
+done
+
+ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${REMOTE_COMMAND} ${quoted_args}"
######################################################################
# Disable globbing to keep wildcards for the remote side
+echo "xremote: -- retrieving files -----------------------------------------"
+
set -f
+if [[ "${ARG_RESULT_DIR}" ]]
+then
+ RESULT_DIR="${ARG_RESULT_DIR}"
+else
+ RESULT_DIR="."
+fi
+
+[[ "${ARG_RESULT_DIR}" ]] && mkdir -p "${ARG_RESULT_DIR}"
+
while read line
do
if [[ "${line}" =~ '@XREMOTE' ]]
then
- label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
- value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
+ label=$(sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/' <<<"${line}")
+ value=$(sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/' <<<"${line}")
case "${label}" in
+ POST)
+ check_remote_is_defined
+ echo "xremote: ${value}"
+ ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
+ ;;
+
GET)
check_remote_is_defined
- ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar c ${value}" | tar mxv
+ ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar 2>/dev/null c ${value}" | tar mxv -C "${RESULT_DIR}"
;;
esac
fi
-done < "${main}"
+
+done < "${main_config}"
set +f
+echo "xremote: -- finished -------------------------------------------------"
+
######################################################################