emacs: How to Capture stdout from dired-do-shell-command? - emacs

In Emacs, is there a way to capture the stdout of dired-do-shell-command, say to the kill-ring?
Without knowing how, I end up going to the Messages buffer and getting the stdout manually from there.

Help for that command says that output goes to a buffer named *Shell Command Output*, assuming command has not &. If this is the case, this piece of code will do what you want:
(defun do-shell-and-copy-to-kill-ring (command &optional arg file-list)
(interactive
(let ((files (dired-get-marked-files t current-prefix-arg)))
(list
(dired-read-shell-command "! on %s: " current-prefix-arg files)
current-prefix-arg
files)))
(dired-do-shell-command command arg file-list)
(with-current-buffer "*Shell Command Output*"
(copy-region-as-kill (point-min) (point-max))))
For async commands, you need to wait for them and look in *Async Shell Command* buffer.

Related

Launching shell command in Eshell without prompt interfence

I'm trying to customize my eshell to intercept python to do two things:
Open a new window and run python if no arguments are given (e.g., $ python)
Run the command "per usual" if arguments are given (e.g., $ python foobar.py)
So far I have something like this
(defun eshell/python (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(message "Use '*python' to call python directly")))
I've tried replacing (message ...) with a few different things:
Based on the ouput of eshell-parse-command "python test.py" I tried
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))
but it hits a recursion limit.
Since *python test.py does what I want, I then tried
(progn (eshell-trap-errors
(eshell-named-command (concat "*" "python") cmd-args)))
but that puts the python process in the background and interrupts stdout with the output of my eshell-prompt-function.
Finally, I've fiddled with shell-command but I can't get it to write to the eshell buffer. In particular,
(progn (eshell-trap-errors
(shell-command (mapconcat (lambda(x) x) cmd-args " ")
(get-buffer "*eshell*") (get-buffer "*eshell*"))))
gives me a Text is read only message and moves point to start of the eshell buffer.
Is what I'm looking for possible?
Edit 1
Running without eshell/python defined, I've instead tried to avoid alias problems:
(defun eshell/gvr (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))))
If test.py is
print "Hello World"
x = raw_input("What should I repeat? ")
print x
running gvr test.py in eshell fails when I reply to the prompt because eshell tries to execute the input instead of handing it to python, but running python test.py goes off without a hitch.
How can I get run my own subprocesses in eshell the same way that they happen by default?

Run commands in Emacs asynchronously, but display output incrementally

I have a utility function:
(defun execute-in-buffer (command-with-args buffer)
"Execute a string COMMAND-WITH-ARGS representing a shell command with arguments,
inserting the results in BUFFER."
(switch-to-buffer buffer)
(insert (format ">>> %s\n" command-with-args))
(let* ((command-args-list (s-split " " command-with-args))
(command (car command-args-list))
(args (cdr command-args-list)))
(apply 'call-process command nil buffer t args)))
This allows me to do things like (execute-in-buffer "ls /" (get-buffer-create "*my-output*"). However, it's not well suited for slow commands. If I call a series of slow commands, I don't get any output until the very end:
(let ((buf (get-buffer-create "*my-output")))
(execute-in-buffer "sleep 10" buf)
(execute-in-buffer "ls /" buf))
I want to be able to call synchronously, so the next command only runs after the previous one finishes. However, I want to see the output from my commands as they run. How would I do this?
(Example code is just for show, I'm happy to drop it in favour of something else.)
Using synchronous processes
If you want to stick with synchronous processes (e.g. using call-process), you need to call (redisplay) after each call to execute-in-buffer for the display to be updated and the output to be visible (also see this question for more details). However, the output of each command will not be visible until the process terminates, and emacs will hang while the external processes are running.
Using asynchronous processes
Using asynchronous processes is a bit more complicated, but avoids hanging Emacs while the commands are running, which also solves the redisplay issue. The tricky part here is to sequentially chain all commands. Here is a bit of elisp which should do the trick:
(defun execute-commands (buffer &rest commands)
"Execute a list of shell commands sequentially"
(with-current-buffer buffer
(set (make-local-variable 'commands-list) commands)
(start-next-command)))
(defun start-next-command ()
"Run the first command in the list"
(if (null commands-list)
(insert "\nDone.")
(let ((command (car commands-list)))
(setq commands-list (cdr commands-list))
(insert (format ">>> %s\n" command))
(let ((process (start-process-shell-command command (current-buffer) command)))
(set-process-sentinel process 'sentinel)))))
(defun sentinel (p e)
"After a process exited, call `start-next-command' again"
(let ((buffer (process-buffer p)))
(when (not (null buffer))
(with-current-buffer buffer
;(insert (format "Command `%s' %s" p e) )
(start-next-command)))))
;; Example use
(with-current-buffer (get-buffer-create "*output*") (erase-buffer))
(execute-commands "*output*"
"echo 1"
"sleep 1"
"echo 2; sleep 1; echo 3"
"ls /")
This works for me:
(async-shell-command "echo 1; sleep 10; echo 2; sleep 10; ls /" "*abcd*")
This can be adapted to do what you need?

emacs shell-mode follows its default-directory to current-file

When I enter cd c:/dir/to/path in shell mode, shell mode follows its default-directory to c:/dir/to/path and that's good. But while visiting a certain file(c:/another/dir/file.ext), how can I let the existing shell directory to that one without entering cd c:/antoher/dir/file.ext ?
Is there any pre-exisiting function for that in emacs? searched quite a lot but not found unfortunately.
I'm using Emacs 24.2.1 in Win7.
EDIT:
I've written a not-good-looking function like followings. Any proposal/advice will be appreciated(I'm a novice in elisp).
(With prefixed interactive call, it will show shell buffer with current directory. I'm repeatedly thinking there might be something already invented one better than this).
(defun my-shell-with-current-directory (&optional arg)
(interactive "P")
(let* ((sp (get-process "shell"))
(spbuf (and sp (process-buffer sp)))
(dir (if buffer-file-name (file-name-directory buffer-file-name) default-directory)))
(if (and arg sp spbuf dir)
(progn
(comint-simple-send sp (concat "cd /d " dir))
(display-buffer spbuf)
(save-excursion
(set-buffer spbuf)
(cd dir)
)
)
(progn
(shell)
(comint-simple-send sp "setlocal enableextensions")
)
)
)
)
You may set default-directory using setq at any time.
You may add that code as a hook onto find-file-hooks.
Also you may advice find-file command with the setting wished.
So far just my thoughts...

Update multi-term buffer name based on PWD

If I use konsole or other terminal, the terminal tag name can change based on PWD. But in multi-term, the buffer name is *terminal<number>*. This is not very nice. Because when I switch between them, the name is not very informative. So I want to rename it based on PWD.
I find that the Enter key is bind to term-send-raw, so I write a function
(defadvice term-send-raw (around rename-term-name activate)
(progn
(rename-buffer
(concat "⇒ "
(shell-command-to-string "pwd | xargs basename | tr -d '\n'")
(format-time-string " [%M ∞ %S]")))
ad-do-it))
But the problem is pwd command return the PWD of the terminal buffer, while it is not the PWD of the SHELL in that terminal.
The PWD of the terminal buffer is set by defcustom multi-term-default-dir. And it does not change when the PWD change in the SHELL.
(defcustom multi-term-default-dir "~/"
"The default directory for terms if current directory doesn't exist."
:type 'string
:group 'multi-term)
How can I get the PWD of the SHELL in the terminal?
Regards.
AFAIK there is no easy way to retrieve information from a running process.
But if you want to get the current directory you could:
ask the shell to print it
parse and trace the command-line for functions like cd, pushd, popd…
poll /proc/PID/cwd
The first method is described in the header of term.el (M-xfind-libraryRETtermRET).
And now, thank you for your question, you gave me the opportunity to do this:
(defadvice term-send-input (after update-current-directory)
(let* ((pid (process-id (get-buffer-process (current-buffer))))
(cwd (file-truename (format "/proc/%d/cwd" pid))))
(cd cwd)))
(ad-activate 'term-send-input)
It's a naive implementation of the third method and it doesn't work if the user uses su or ssh. However, I don't know if it's possible withouth using the first or the second method.
In your case, you can just replace the cd command with whatever you want.
Building off of Daimrod's answer for polling /proc/PID/cwd, I found a way get around the problem that Reed pointed out where the advice doesn't pick up the updated CWD immediately and you have to hit Enter twice.
If you move the CWD update code to its own function and use run-at-time to call it from the advice at a later time, it will pick up the updated CWD correctly. Unfortunately I don't know enough about Emacs' scheduling to explain why this works (any enlightenment would be appreciated).
Here's my code based on Daimrod's. Note I advised term-send-input for line-mode and term-send-return for char-mode. I tested this using multi-term on Emacs 24.3.1:
(defadvice term-send-input (after update-current-directory)
(run-at-time "0.1 sec" nil 'term-update-dir)
)
(ad-activate 'term-send-input)
(defadvice term-send-return (after update-current-directory)
(run-at-time "0.1 sec" nil 'term-update-dir)
)
(ad-activate 'term-send-return)
(defun term-update-dir ()
(let* ((pid (process-id (get-buffer-process (current-buffer))))
(cwd (file-truename (format "/proc/%d/cwd" pid))))
(unless (equal (file-name-as-directory cwd) default-directory)
(message (concat "Switching dir to " cwd))
(cd cwd)))
)
Most terminals get their window name from the command echo -en. In zsh you can put this in your ~/.zshenv
precmd() { echo -en "\e]0;`basename ${PWD}`\a" }
and that will get the basename of your PWD environment variable. Ideally multi-term would do something similar and put it in multi-term-buffer-name, which is the variable which holds its buffer name.
Yes, this is not a complete solution. I'm hoping for one too!
Try this:
(defun open-or-jump-to-multi-term ()
(interactive)
(if (string-prefix-p "*terminal<" (buffer-name))
(delete-window)
(progn
(setq bufname (concat "*terminal<" (directory-file-name (file-name-directory (buffer-file-name))) ">"))
(if (get-buffer-process bufname)
(switch-to-buffer-other-window bufname)
(progn
(split-window-right)
(other-window 1)
(multi-term)
(rename-buffer bufname)
)
)))
)
(global-set-key (kbd "C-`") 'open-or-jump-to-multi-term)

emacs equivalent of following vi command

I am looking for equivalent of following vi command
:! nl %
this runs nl command on currently open file
What is emacs way to detect name of open file ?
M-X shell-commnad nl
I am not able find determine value of current open/buffer and substitute.
Thx/Mahesh
EDIT: Misread your question as wanting to apply that change to the file you're working on. If you just want to run a shell command against a buffer, you can use shell-command-on-region, which is usually bound to M-|.
If you're just trying to get to a particular line number, M-x goto-line works. I bind that to C-x C-l by putting (define-key global-map "\C-x\C-l" 'goto-line) in my ~/.emacs.
Try this (in your ~/.emacs file):
;;; Run a shell command on all text between the mark and the point and
;;; replace with the output.
(defun shell-command-in-region (start end command &optional flag interactive)
"Execute shell-command-on-region and replace the region with the output
of the shell command."
(interactive (list (region-beginning) (region-end)
(read-from-minibuffer "Shell command in region: "
nil nil nil 'shell-command-history)
current-prefix-arg
(prefix-numeric-value current-prefix-arg)))
(shell-command-on-region (point) (mark) command t)
)
(define-key esc-map "#" 'shell-command-in-region)
Invoke it by selecting a region you want to operate on and then doing M-#.
If you always want the buffer's file name to be inserted for the shell command, you can use this advice:
(defadvice read-shell-command (before read-shell-command-with-filename activate)
"force the initial contents to contain the buffer's filename"
(if (and (null (ad-get-arg 1))
buffer-file-name)
(ad-set-arg 1 buffer-file-name)))
Once you've added the above code, M-x shell-command will always start with the buffer's file name, so you can use it in the command.
I use this:
(defun my-shell-command-on-current-file (command &optional output-buffer error-buffer)
"Run a shell command on the current file (or marked dired files).
In the shell command, the file(s) will be substituted wherever a '%' is."
(interactive (list (read-from-minibuffer "Shell command: "
nil nil nil 'shell-command-history)
current-prefix-arg
shell-command-default-error-buffer))
(cond ((buffer-file-name)
(setq command (replace-regexp-in-string "%" (buffer-file-name) command nil t)))
((and (equal major-mode 'dired-mode) (save-excursion (dired-move-to-filename)))
(setq command (replace-regexp-in-string "%" (mapconcat 'identity (dired-get-marked-files) " ") command nil t))))
(shell-command command output-buffer error-buffer))
(global-set-key (kbd "M-!") 'my-shell-command-on-current-file)
Then you can do M-! nl %