UPDATE
I have accepted #Sean answer, with some small modifications.
(defun sudo-shell-command (buffer password command)
(let ((proc (start-process-shell-command
"*sudo*"
buffer
(concat "sudo bash -c "
(shell-quote-argument command)))))
;;; Added to #Sean answer to display the passed buffer
(display-buffer buffer '((display-buffer . nil)) nil)
(process-send-string proc password)
(process-send-string proc "\r")
(process-send-eof proc)))
(defun sudo-bundle-install (password)
(interactive (list (read-passwd "Sudo password for bundle install: ")))
(let ((default-directory (concat default-directory
"./fixtures/test-kitchen-mode-test-run/"))
;;; Added from accepted answer below by #sean
;;; need a buffer to display process in.
(generated-buffer (generate-new-buffer "*test-kitchen-test-setup*")))
(sudo-shell-command
;;; pass reference to generated process buffer by name.
;;; Need to add a defun to get the current test-kitchen buffer
;;; if it exists to use, but that is outside the scope of the question.
(buffer-name generated-buffer)
password
"bundle install; bundle exec berks install")
(clear-string password)))
Say I need to call a process in elisp, and that process requires sudo priveleges. Example, running Ruby's bundle install:
(let ((generated-buffer (generate-new-buffer "*test-kitchen-test-setup*")))
(display-buffer generated-buffer '((display-buffer . nil)) nil)
(call-process-shell-command
(concat "cd " (concat default-directory "./fixtures/test-kitchen-mode-test-run")
"; sudo bundle install; sudo bundle exec berks install;")
nil generated-buffer t))
The bundle command requires sudo to install gems correctly. How can I call this shell command in elisp with sudo, enter the password, and still be able to display the results in the generated window?
Here's a helper function that executes a single shell command as sudo with a supplied password:
(defun sudo-shell-command (buffer password command)
(let ((proc (start-process-shell-command
"*sudo*"
buffer
(concat "sudo bash -c "
(shell-quote-argument command)))))
(process-send-string proc password)
(process-send-string proc "\r")
(process-send-eof proc)))
You might apply it to your situation like so:
(defun sudo-bundle-install (password)
(interactive (list (read-passwd "Sudo password for bundle install: ")))
(let ((default-directory (concat default-directory
"./fixtures/test-kitchen-mode-test-run/")))
(sudo-shell-command
"*test-kitchen-test-setup*"
password
"bundle install; bundle exec berks install")
(clear-string password)))
You need to create a file with your password:
(defun my-run-command-under-sudo (password command &rest cpsc-args)
"Run COMMAND under sudo with your PASSWORD.
Other arguments are passed to `call-process-shell-command'
and should start with BUFFER (the output destination).
NB: this is _not_ secure: it creates a temp file with your password."
(let ((password-file (make-temp-file "elisp-sudo")))
(with-temp-file password-file (insert password))
(unwind-protect
(apply 'call-process-shell-command
(concat "sudo " command)
password-file cpsc-args)
(delete-file password-file))))
Now you can do:
(let ((default-directory (concat default-directory "./fixtures/test-kitchen-mode-test-run")))
(my-run-command-under-sudo "my-password" "bundle install" t)
(my-run-command-under-sudo "my-password" "bundle exec berks install" t))
Related
For example, I have this setup in my .emacs
(defun gtags-create-or-update ()
"Create or update the gnu global tag file."
(interactive)
(if (y-or-n-p-with-timeout
(format "Run gtags to create/update tag file for code at %s (default no)? "
default-directory)
5 nil) ; default - no run
(unless (= 0 (call-process "global" nil nil nil " -p")) ; tagfile doesn't exist?
(let ((olddir default-directory)
(topdir (read-directory-name
"gtags: top of source tree: " default-directory)))
(cd topdir)
(shell-command "gtags -v")
;; (shell-command "gtags && echo 'created tagfile'")
(cd olddir)) ; restore
;; tagfile already exists; update it
(shell-command "global -uv"))))
;; (shell-command "global -u && echo 'updated tagfile'")))
(add-hook 'c-mode-common-hook
(lambda ()
(require 'gtags)
(gtags-mode t)
(gtags-create-or-update)))
When I run gtags-create-or-update explicitly, emacs prompt in the minibuffer to ask me whether to create/update tag files. However, when I open a c/cpp file, it always pops up a GUI window ask me yes or no, which is super annoying. I am wondering why this is happening.
What #phils says in the comment. To avoid dialog boxes in GUI Emacs, you can set the use-dialog-box to nil Put the line (setq use-dialog-box nil) in your initialization file.
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)
Right now I am using the following to compile, when I'm in for example main.cpp
C-x b Makefile RET M-x compile RET RET
I actually have M-x compile as a keyboard shortcut, but the problem is I would really like not having to go through all that trouble to simply run my Makefile.
I need to visit Makefile to make sure the compile command is executed using the same directory. Is there any way to pin the directory so I can simply go M-x compile RET RET?
Best regards
Use recompile instead. C-u M-x recompile will let you edit the compile command first. Either way the compile will work out of the directory the last compile was done in.
See my answer here
Directory local variables provide an easy way to trigger the compile from a parent directory of any source file in a subdirectory.
I run emacs primarily on windows.
When I have a makefile that is in a parent directory of a C module, I use this as the compile command:
cd .. && nmake <arguments here>
for example:
cd .. && nmake CONFIG=Debug PLATFORM=x64 target
Beyond that, I find that specifying the make command line that I want to run for various modules is sort of a pain. I wanted a way to attach the default compile command to the buffer being edited. So I wrote a little elisp to handle that job. I figured to insert into the header comments of each buffer a line that would stipulate my preferred compile command, like this:
compile: cd .. && nmake CONFIG=Debug PLATFORM=x64 target
And then have a piece of elisp run, before I invoke M-x compile that grabs the line and proposes it as the compile command I would like to run.
This defun pulls a line out of the header comments:
(defun cheeso-c-get-value-from-comments (marker-string line-limit)
"gets a string from the header comments in the current buffer.
This is used to extract the compile command from the comments. It
could be used for other purposes too.
It looks for \"marker-string:\" and returns the string that
follows it, or returns nil if that string is not found.
eg, when marker-string is \"compile\", and the following
string is found at the top of the buffer:
compile: cl.exe /I uthash
...then this command will return the string
\"cl.exe /I uthash\"
It's ok to have whitespace between the marker and the following
colon.
"
(let (start search-limit found)
;; determine what lines to look in
(save-excursion
(save-restriction
(widen)
(cond ((> line-limit 0)
(goto-char (setq start (point-min)))
(forward-line line-limit)
(setq search-limit (point)))
((< line-limit 0)
(goto-char (setq search-limit (point-max)))
(forward-line line-limit)
(setq start (point)))
(t ;0 => no limit (use with care!)
(setq start (point-min))
(setq search-limit (point-max))))))
;; look in those lines
(save-excursion
(save-restriction
(widen)
(let ((re-string
(concat "\\b" marker-string "[ \t]*:[ \t]*\\(.+\\)$")))
(if (and start
(< (goto-char start) search-limit)
(re-search-forward re-string search-limit 'move))
(buffer-substring-no-properties
(match-beginning 1)
(match-end 1))))))))
Ok, now I need something to invoke that before I invoke compile.
(defun cheeso-invoke-compile-interactively ()
"fn to wrap the `compile' function. This simply
checks to see if `compile-command' has been previously set, and
if not, invokes `cheeso-guess-compile-command' to set the value.
Then it invokes the `compile' function, interactively."
(interactive)
(cond
((not (boundp 'cheeso-local-compile-command-has-been-set))
(cheeso-guess-compile-command)
(set (make-local-variable 'cheeso-local-compile-command-has-been-set) t)))
;; local compile command has now been set
(call-interactively 'compile))
Then of course, the defun that guesses the compile command:
(defun cheeso-guess-compile-command ()
"set `compile-command' intelligently depending on the
current buffer, or the contents of the current directory."
(interactive)
(set (make-local-variable 'compile-command)
(cond
(buffer-file-name
(let ((filename (file-name-nondirectory buffer-file-name)))
(cond
;; editing a C-language source file - check for an
;; explicitly-specified command
((string-equal (substring buffer-file-name -2) ".c")
(let ((explicit-compile-command
(cheeso-c-get-value-from-comments "compile" 34)))
(or explicit-compile-command
(concat "nmake " ;; assume a makefile exists
(file-name-sans-extension filename)
".exe"))))
;; editing a makefile - just run nmake
((string-equal (substring buffer-file-name -8) "makefile")
"nmake ")
;; something else - do a typical .exe build
(t
(concat "nmake "
(file-name-sans-extension filename)
".exe")))))
(t
;; punt
"nmake "))))
The final bit is to bind C-x C-e , normally bound to compile, to the wrapper defun:
(global-set-key "\C-x\C-e" 'cheeso-invoke-compile-interactively)
Now, when I do C-x C-e in the buffer, it searches for the compile command, and proposes to me the command that it finds. I can edit the proposed compile command, then press ENTER and run it.
I'm using Fsharp mode in emacs.
The key of ^C x is mapped to Run ... command which is as follows.
(defun fsharp-run-executable-file ()
(interactive)
(let ((name (buffer-file-name)))
(if (string-match "^\\(.*\\)\\.\\(fs\\|fsi\\)$" name)
(shell-command (concat (match-string 1 name) ".exe")))))
The problem is that it tries to run bash something.exe, whereas I need to run the command of mono something.exe. I got error message of /bin/bash ...exe: cannot execute binary file.
How can I come up with a new elisp command to launch mono, and then get the result to show it to *compilation* buffer?
You could try changing the last line to:
(shell-command (concat "mono " (match-string 1 name) ".exe")))))
but I haven't tested this.
You can redefine fsharp-run-executable-file and use this one instead:
(defun fsharp-run-executable-file ()
(interactive)
(let ((name (buffer-file-name)))
(if (string-match "^\\(.*\\)\\.\\(fs\\|fsi\\)$" name)
(compile (concat "mono " (match-string 1 name) ".exe")))))
There are two changes: 1) concat mono before the command (as petebu wrote); 2) use the compile function so that the output is in the *compilation* buffer.
To test quickly, just evaluate the above function (add it in your Emacs init file for a permanent change). Note that you shouldn't modify fsharp.el file, at I might update at some point (you don't want to lost your changes).
Edit
One issue with the previous function is that it modifies the last compilation command. This might might annoying if you compile your code with compile or recompile commands. Here is a fix:
(defun fsharp-run-executable-file ()
(interactive)
(let ((name (buffer-file-name)))
(if (string-match "^\\(.*\\)\\.\\(fs\\|fsi\\)$" name)
(compilation-start (concat "mono " (match-string 1 name) ".exe")))))
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 %