When I launch an ansi-term in a remote (TRAMP) buffer, I'd like the resulting shell to reflect the current default-directory. That is, I'd like ansi-term to give me a remote shell, so I can do things in the directory of the current file.

3 Answers 3


This is not exactly what you are searching for, but it gives you a very similar functionality, albeit outside of Emacs and not in ansi-term. So, I still decided to submit it.

For my own convenience (and because I still like the native OS shell a lot), I implemented the possibility to open a gnome-terminal set to the current default-directory. Will be easy to adapt to any other shell.

This also works with remote directories:


(defun dfeich/gnome-terminal (&optional path)
  "Opens a gnome terminal at PATH. If no PATH is given, it uses
the value of `default-directory'. PATH may be a tramp remote path."
  (unless path (setq path default-directory))
  (if (tramp-tramp-file-p path)
      (let ((tstruct (tramp-dissect-file-name path)))
     ((equal (tramp-file-name-method tstruct) "ssh")
      (call-process "/usr/bin/gnome-terminal" nil nil nil
            "-x" "sh" "-c"
            (format "ssh -t %s 'cd %s; exec bash'; exec bash"
                (tramp-file-name-host tstruct)
                (tramp-file-name-localname tstruct))))
     (t (error "not implemented for method%s"
           (tramp-file-name-method tstruct)))))
    (call-process "/usr/bin/gnome-terminal" nil nil nil
          "-x" "sh" "-c"
          (format "cd %s;exec bash" path))))

You can't do this directly with the ansi-term command, but you can define an equivalent command using the approach demonstrated by dfeich, which might look something like:

(defun my-ansi-term (command &optional name)
  "Runs COMMAND in a `term' buffer."
   (let ((shell (or explicit-shell-file-name
                    (getenv "ESHELL")
                    (getenv "SHELL")
     (if (tramp-tramp-file-p default-directory)
         (let ((tstruct (tramp-dissect-file-name default-directory)))
           (when (string= (tramp-file-name-method tstruct) "ssh")
             (setq suggestion
                   (apply #'format "ssh -t %s@%s -p %s cd %s; exec %s"
                          (mapcar #'shell-quote-argument
                                  (list (tramp-file-name-user tstruct)
                                        (tramp-file-name-host tstruct)
                                         (tramp-file-name-port tstruct))
                                        (tramp-file-name-localname tstruct)
             (setq name (concat "ansi-term: " default-directory))))
       (setq suggestion shell
             name "ansi-term"))
     (list (read-from-minibuffer "Run program: " suggestion)
  (let* ((name (or name command))
         (switches (split-string-and-unquote command))
         (command (pop switches))
         (termbuf (apply 'make-term name command nil switches)))
    (set-buffer termbuf)
    (switch-to-buffer termbuf)))

Tramp almost certainly provides a better way to generate the ssh command line from the parsed filename, but I couldn't find something appropriate. Also, assuming the local shell is valid for the remote host is clearly not correct in all situations.

As such, this function doesn't account for all use-cases by any means; but I would guess that it probably works for most.

  • In fact, without getting tramp to generate the ssh command, this is pretty flawed. Multi-hops and other tramp proxy use-cases are a complete fail, for instance. Still, as previously mentioned, this will work for lots of cases. I'd be keen to know the correct way to build the ssh command line, though.
    – phils
    Commented Apr 9, 2016 at 15:43
  • I think I've found a way to get tramp to handle all of the process communication, see my answer.
    – PythonNut
    Commented Apr 9, 2016 at 18:04

As it turns out, this can be achieved by modifying term-exec-1. (comments removed for brevity)

(defun nadvice/term-exec-1 (name buffer command switches)
  (let* ((environment
           (format "TERM=%s" term-term-name)
           (format "TERMINFO=%s" data-directory)
           (format term-termcap-format "TERMCAP="
                   term-term-name term-height term-width)
           (format "EMACS=%s (term:%s)" emacs-version term-protocol-version)
           (format "INSIDE_EMACS=%s,term:%s" emacs-version term-protocol-version)
           (format "LINES=%d" term-height)
           (format "COLUMNS=%d" term-width)))
          (append environment
          (append environment
         (process-connection-type t)
         (coding-system-for-read 'binary))
    (apply 'start-file-process name buffer
       "/bin/sh" "-c"
       (format "stty -nl echo rows %d columns %d sane 2>/dev/null;\
if [ $1 = .. ]; then shift; fi; exec \"$@\""
           term-height term-width)
       command switches)))

(advice-add 'term-exec-1 :override #'nadvice/term-exec-1)

As phils notes, this still has some issues (such as not correctly detecting the remote shell under certain circumstances), but it's more robust overall. For example, it should work with all tramp methods that support asynchronous processes, which includes, su and sudo.


I should note that I did need to remove (inhibit-eol-conversion t) from the let, because the newlines were being incorrectly displayed in remote sessions. So far, I haven't seen anything break, but makes me ill-at-ease.

Path forward

I feel like this is generic enough to be submitted as a patch with a bit more work, but I'd honestly like to get bug#19455 (which I submitted a patch for) squared away first.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.