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:
using
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).using
start-file-process
as 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)))))))
yielding output:
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).