To see this in action: $ touch foo $ emacs -Q M-x eshell cp foo bar -s ls -l bar `bar' should be a symlink pointing to `foo', but it's just a regular file. This happens because, when :preserve-args is true, `eshell-eval-using-options' doesn't flatten and stringify the args to be parsed. That results in the various helper functions modifying the list of raw args in-place. When :external is set, if `eshell--process-option' finds an option it doesn't recognize, it throws, telling `eshell--do-opts' to run the external command instead. That requires having the original args, un-tampered with, in order to forward on to the external command. Attached is a patch that fixes this by copying the raw args list for the :preserve-args case before manipulating it. I considered only adding this when :external is also set, but I think it's useful for callers to be able to inspect the raw args afterwards. This is used by `eshell/su' to check for the presence of a "-" argument, since `eshell-eval-using-options' doesn't handle that. (It might be nice to fix that too, but I think it's still useful to keep the original raw args around unchanged. Improving "-" support could be done later.) As part of this, I also added several unit tests for `eshell/su' and `eshell/sudo' to make sure I didn't break anything there; these should pass both before and after the rest of the patch. I also added tests to make sure the original arguments are preserved for calling the external command; these should fail before the patch, and pass after. There's just one remaining issue that I'm not sure how to fix: since I updated the `eshell-eval-using-options' macro, any file that uses it needs to be recompiled to get the new version. What's the right way to tell the build system about this? I just ran `touch lisp/eshell/*.el' locally, but I'm sure there's a better way so that people can just run `make' after pulling this patch.