Emacs workflow to edit Bash scripts while they run

M-x delete-file, press down to insert obtain the path to the file you're editing, and RET to delete the file. When you next save your buffer, this will create a new file. Bash will keep executing the old, deleted file.

By the way, in most cases, you don't even need to take this precaution. Emacs usually saves to a temporary file, then renames that temporary file to the proper file name (which deletes the old file, unless it's being kept as a backup). Emacs only overwrites the file in place if it detects that it can't recreate the file properly: if the file has hard links, or if you don't have write permission on the directory. (I'm simplifying a little; the details are in the basic-save-buffer-2 function in files.el.) As long as the file has a single directory entry and you have write permissions on the directory containing the script, just press C-x C-s.


[edit] The best workaround is done in shell script side, not in Emacs. In short, _put all in a brace, and put "exit" before the closing brace. Read the linked answer well to avoid pitfalls.

In this reply, I supplement Gilles's answer with theory.

The reason deletion is safe but modification is not is that in Unix, a deleted file gets inaccessible from the file system, but the processes that had opened the file (here, /bin/bash) can still read it, as explained here. It's not that bash does something special, like buffering the entire file, but it simply reads. (Even after the deletion, you can recover the script while it's running, as explained here. In bash, the script seems to be always opened as 255, /proc/<pid>/fd/255.)

[edit] My old answer included Emacs solution and co, as per OP's request. But after years of experience, I'm sure that the above pointer is the best, far better than any other attempts.)


i already had a function for renaming the current buffer's file. was a short jump to a function which made it easy to toggle to a temp version of a file and then back again. basically, if you run this function when visiting a normal file "foo.txt", it will copy the file to a new file in the same dir with the name "tmp.foo.txt". edit as desired. when ready to switch back, run the function again and it will move the temp file back over the original file. i used the prefix "tmp." instead of a suffix because most mode recognition in emacs is based on the suffix (you could probably do something fancier with remembering all the buffer modes and re-applying...).

(defun toggle-temp-file ()
  (interactive)
  (let* ((cur-buf (current-buffer))
         (cur-file (buffer-file-name cur-buf))
         (cur-file-dir (file-name-directory cur-file))
         (cur-file-name (file-name-nondirectory cur-file))
         new-file)
    (if (string-match "^tmp\\.\\(.+\\)\\'" cur-file-name)
        ;; temp file -> orig file
        (progn
          (setq new-file
                (concat cur-file-dir (match-string 1 cur-file-name)))
          (rename-file cur-file new-file t))
      ;; orig file -> temp file
      (setq new-file (concat cur-file-dir "tmp." cur-file-name))
      (copy-file cur-file new-file nil nil t))
    (kill-buffer cur-buf)
    (find-file new-file)))

Tags:

Emacs

Bash