In the end, I used the async-shell-command-to-string
by Johan Lindström, which I found on the emacs mailing list:
(require 'cl)
(defun async-shell-command-to-string (command callback)
"Execute shell command COMMAND asynchronously in the
background.
Return the temporary output buffer which command is writing to
during execution.
When the command is finished, call CALLBACK with the resulting
output as a string."
(lexical-let
((output-buffer (generate-new-buffer " *temp*"))
(callback-fun callback))
(set-process-sentinel
(start-process "Shell" output-buffer shell-file-name shell-command-switch command)
(lambda (process signal)
(when (memq (process-status process) '(exit signal))
(with-current-buffer output-buffer
(let ((output-string
(buffer-substring-no-properties
(point-min)
(point-max))))
(funcall callback-fun output-string)))
(kill-buffer output-buffer))))
output-buffer))
(provide 'async-shell-command-to-string)
my code then becomes:
(defun fr-wordreference-word-at-point ()
"Looks up word in point using Wordreference."
(interactive)
(let* ((word (asciify-string (downcase (current-word t)))))
(async-shell-command-to-string
(concat "wr.sh " word)
(lambda (s)
(save-excursion
(set-buffer (get-buffer-create "*wr*"))
(erase-buffer)
(insert s)
(display-buffer "*wr*" t))))))
which seems to work just fine.