I would like to be able to manage external processes from Emacs, in a way that is:
- (a) safe w.r.t. arguments escaping for the shell,
- (b) efficient both for local and remote processes (using TRAMP w/ ssh).
Here are two solutions I can think of to implement calling a possibly remote process:
shell-command: this has the advantage that it is fast (meets criterion b), but one needs to pass a full shell command (fails for criterion a).
start-file-processas recommended by the TRAMP documentation: this is nice in terms of arguments handling (criterion a), but a new SSH connection seems to be called for each process, which dramatically slows things down in remote configurations (criterion b).
NB: these solutions also differ in that (1) is synchronous whereas (2) is asynchronous, but I don't mention it because (a)synchronicity doesn't matter for my application.
It looks like option (2) is slow because it spawns a new ssh process, whereas option (1) reuses an already existing one. Is there any high-level function allowing to call a possibly remote process, while still being efficient by reusing an existing connection if possible?
Below is the code I used to benchmark both solutions:
(let ((test/remote-buffer (find-file "/xxx.xxx.xxx.xxx:/home/LOGIN/")) (test/output-buffer (get-buffer-create "*hostname*"))) (with-current-buffer test/output-buffer (erase-buffer)) (with-current-buffer test/remote-buffer (setq test/begin (current-time)) (shell-command "hostname" test/output-buffer) (message "1-Elapsed: %f" (float-time (time-subtract (current-time) test/begin)))) (with-current-buffer test/remote-buffer (setq test/begin (current-time)) (set-process-sentinel (start-file-process "hostname" test/output-buffer "hostname") (lambda (p e) (message "2-Elapsed: %f" (float-time (time-subtract (current-time) test/begin)))))))
1-Elapsed: 0.010178 Tramp: Opening connection for xxx.xxx.xxx.xxx using scp... Tramp: Sending command `exec ssh -q -o ControlPath=/tmp/tramp.12155IuC.%r@%h:%p -o ControlMaster=auto -e none xxx.xxx.xxx.xxx' Tramp: Waiting for prompts from remote shell...done Tramp: Found remote shell prompt on `xxx.xxx.xxx.xxx' Tramp: Opening connection for xxx.xxx.xxx.xxx using scp...done 2-Elapsed: 1.116449
In the case of local processes, both implementations take a similar elapsed time, which is negligible (and often faster for solution 2).