1

I wish there was a hook, that every time I did

M-x shell

it would send "cd " . (pwd) . "\n" to an already running M-x shell, and then switch windows to that shell.

That way I wouldn't need to kill a running *shell* buffer first, just so it would cd to where I now want to use it.

Note I am not talking about tracking. Tracking is not a problem.

You see, it's all about me being lazy.

Let's say I do M-x shell, use it for five minutes, then forget about it.

OK, now 1/2 an hour later, in a different directory, I do M-x shell again.

But then I need to do

$ cd ../newdir/blurp

there in the shell, to change its current directory to here, where I plan to use it.

Or I can do C-x C-k *shell* and then do M-x shell to start a fresh one, already cd'ed to $PWD. But that is rather "wasteful", killing a shell just because I was too lazy to type "cd ../newdir/blurp" into it.

2
  • Does using C-u M-x shell help when it comes to opening a different buffer? Commented May 22 at 8:50
  • Interesting. But... I wish to just keep reusing the same single *shell* buffer. I need to "advise" the M-x shell function to simply type in that cd pwd line each time I call it. In case I don't want it to run my line, I can instead just use C-x C-b to go to it. Commented May 22 at 13:05

3 Answers 3

1

Here's an update from danshell that fixes some issues:

(defun updated-danshell ()
  "Start a new shell or navigate an existing shell to the current folder."
  (interactive)
  (let ((buffer (get-buffer "*shell*"))
        (folder default-directory)
        (command (concat "cd "
                         (expand-file-name default-directory)
                         "; echo \"Directory is now: $PWD\"\n")))
    (if buffer
        (progn
          (with-current-buffer buffer
            (shell-cd folder)
            (comint-send-string nil command))
          (switch-to-buffer buffer))
      (shell))))
7
  • Cool! Except it should still check if it is already in the right directory, and if so don't wastefully cd again or print messages. Commented May 31 at 3:42
  • 1
    If someone knows how to identify the current directory used by the shell-mode buffer, please let me know. I don't know how to do that. And gnu.org/software/emacs/manual/html_node/efaq/… seems to indicate it's not currently reliably feasible.
    – PRouleau
    Commented May 31 at 12:04
  • stackoverflow.com/questions/28196228/… says how maybe. Oh. I am saying that 99% of the time the two are the same, as I don't use aliases for 'cd' in the shell etc. So it would be fine to trust the usual functions to tell what the PWD is when comparing. Thanks. Commented Jun 1 at 12:28
  • No. That would give you information about the file name visited in a buffer. A term buffer does not visit a file, just like the scratch buffer. So its buffer-file-name is nil.
    – PRouleau
    Commented Jun 1 at 12:35
  • 1
    @DanJacobson, there is one way: dirtrack-mode where you must have a shell prompt that shows the current directory and then you define a regexp to extract it using a hook. See gnu.org/software/emacs/manual/html_node/emacs/… . I might implement something for my PEL project (github.com/pierre-rouleau/pel) eventually, allowing this to be activated for various terminals and providing customized options for specifying the regexp. But that's too much to describe here.
    – PRouleau
    Commented Jun 2 at 3:05
2

The following works for me:

(defun my/eshell ()
  "Start a new eshell or navigate an existing eshell to the current folder."
  (interactive)
  (let ((buffer (get-buffer "*eshell*"))
    (folder default-directory))
    (if buffer
    (progn
      (with-current-buffer buffer
        (progn
          (shell-cd folder)
          (eshell/clear)
          ))
      (switch-to-buffer buffer))
      (eshell)))
  )

If an eshell buffer exists, its working directory is changed to that of the current buffer and a new prompt is displayed using the clear command. Otherwise a new eshell is started.

Edit Doing the same for an inferior shell would look like:

(defun my/shell ()
  "Start a new shell or navigate an existing shell to the current folder."
  (interactive)
  (let ((buffer (get-buffer "*shell*"))
    (folder default-directory)
    (command (concat "cd " (shell-quote-argument default-directory) "\n")))
    (if buffer
    (progn
      (with-current-buffer buffer
        (progn
          (shell-cd folder)
          (comint-send-string nil command)
          (comint-send-string nil "cls\n")
          ))
      (switch-to-buffer buffer))
      (shell)))
  )

Instead of aliassing it to replace the original would not be my choice. Instead binding it to a key sequence makes it easier to use too:

(global-set-key (kbd "C-c e") 'my/shell)
4
  • OK, I get it. One little problem: because I'm using shell, not eshell, I still also need to inject "cd newdir \n" into the buffer, to also inform the shell. How might I do that? Thanks. Also I would like to alias the whole thing as "shell" so it would be a drop-in replacement. Commented May 22 at 14:49
  • @DanJacobson see edit.
    – rsp
    Commented May 22 at 15:55
  • Works great. Except you don't want "cls": 1. not found on Unix. 2. the user didn't intend for the screen to be cleared. 3. it's tough to clear anyway, even with Unix "clear". By the way, due to the injection the user will see a double prompt. But that isn't too bad.... Pack it all into in one line, Commented May 23 at 0:33
  • Perfect!: ...(folder default-directory) (command (concat "cd " (shell-quote-argument default-directory) "; echo [Directory now $PWD]\n"))) (if buffer... Double prompt gone too! Commented May 23 at 0:35
0

Thanks @rsp for doing 99% of the work. Now perfect:

(defun danshell ()
  "Start a new shell or navigate an existing
shell to the current folder."
  (interactive)
  (let ((buffer (get-buffer "*shell*"))
    (folder default-directory)
    (command (concat "cd "
      (shell-quote-argument default-directory)
      "; echo [Directory now $PWD]\n")))
    (if buffer
    (progn
      (with-current-buffer buffer
        (progn
          (shell-cd folder)
          (comint-send-string nil command)
          ))
      (switch-to-buffer buffer))
      (shell)))
  )
1
  • 1
    Your code still has issues: 1) square brackets inside an unquoted echo command is not allowed in some shells, 2) shell-quoted-argument is new in emacs 29.1, expand-file-name is older and handles ~ properly. 3) you don't need the second progn form, 4) style: don't leave end parens alone in lines, the first docstring line should be complete. Trick: use emacs to help you indent with tab bound to indent-for-tab-command .
    – PRouleau
    Commented May 24 at 11:26

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.