2011-12-01 113 views

回答

8

M-x delete-file,按down插入獲得的路徑,你正在編輯的文件,並RET刪除該文件。當你下一次保存你的緩衝區時,這將創建一個新文件。 Bash將繼續執行舊的已刪除文件。

順便說一句,在大多數情況下,你甚至不需要採取這種預防措施。 Emacs通常保存到一個臨時文件,然後將該臨時文件重命名爲正確的文件名(刪除舊文件,除非它保留爲備份)。如果Emacs檢測到文件無法正確重新創建文件:如果該文件具有硬鏈接,或者您沒有該目錄的寫入權限,則Emacs只會覆蓋該文件。 (我正在簡化一些;細節位於files.elbasic-save-buffer-2函數中。)只要文件具有單個目錄條目,並且您對包含該腳本的目錄具有寫入權限,只需按C-x C-s即可。

+0

這聽起來很簡單。我需要仔細檢查一下,確實有效。如果是這樣,這是否意味着Emacs現有的保存和重命名行爲會阻止此問題出現? –

+0

@MichaelHoffman是的,如果Emacs正在進行保存和重命名(這是大多數情況下的默認行爲),那麼您根本就不會有這個問題。請注意,我假設這裏是一個真正的unix,如果你正在對Cygwin這樣的東西運行bash,那麼你可能無法刪除這個文件(我對此不確定)。 – Gilles

+0

你可以編輯你的答案,包括這個?看起來整個問題是不必要的,因爲Emacs開箱即可做出正確的事情。 –

2

我已經有了重命名當前緩衝區文件的功能。是一個短暫的跳轉到一個功能,它可以很容易地切換到臨時版本的文件,然後再回來。基本上,如果您在訪問普通文件「foo.txt」時運行此函數,它會將該文件複製到名爲「tmp.foo.txt」的相同目錄中的新文件。根據需要編輯。準備切換回來時,再次運行該功能,它會將臨​​時文件移回原始文件。我使用了前綴「tmp」。而不是後綴,因爲emacs中的大多數模式識別都基於後綴(您可以在記住所有緩衝模式並重新應用時做更有趣的事情)。

(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))) 
7

在這個答覆,我

  1. 補充由吉爾漂亮的答案與理論。
  2. 提示emacs使用方面的改進。
  3. 純粹通過shell腳本來建議替代方案。

警告:我不是一個專業程序員。

1理論

缺失的原因是安全的,但改變並非是在Unix中,已刪除的文件變得不可訪問從文件系統,但已經打開的文件的過程(在這裏,/斌/慶典)仍然可以讀取它,如解釋here。並不是說bash做了一些特殊的事情,比如緩衝整個文件,但它只是讀取。 (即使刪除後,你可以在它的運行恢復腳本,as explained here在bash,腳本似乎總是被開闢爲255,/proc/<pid>/fd/255。)

2個Emacs的解決方案

現在emacs的;爲了安全起見,你可以自動化所有;添加到before-save-hook一個函數來檢查它是否是SH-模式,如果腳本是打開自動刪除,用lsof檢查:

(defvar delete-open-sh-flag nil 
    "Used by `delete-open-sh.` If nil, deletion didn't happen. 
Otherwise, the mode is saved.") 

(defun delete-open-sh() 
    (setq delete-open-sh-flag nil) 
    (when (and (eq major-mode 'sh-mode) 
     (buffer-file-name) 
     (file-exists-p (buffer-file-name))) 
    (let ((buf (generate-new-buffer " *foo*"))) 
     (call-process "lsof" nil; infile 
      buf ; dest 
      nil ; display 
      (buffer-file-name)) 
     (unless (eq 0 (buffer-size buf)) 
     (setq delete-open-sh-flag (file-modes (buffer-file-name))) 
     (delete-file (buffer-file-name))) 
     (kill-buffer buf)))) 

(defun delete-open-sh-after() 
    (when delete-open-sh-flag 
    (set-file-modes (buffer-file-name) delete-open-sh-flag))) 

(add-hook 'before-save-hook 'delete-open-sh) 
(add-hook 'after-save-hook 'delete-open-sh-after) 

但這是不完美的。它保存了文件模式,但僅此而已。

3純bash的解決方案

或者,你可以讓bash腳本本身做刪除。使用此:

# Usage: . /path/to/this-script (Always use abs path) 

# Restart the caller script itself in a temporary file; this allows 
# editing of the caller script file while it's run. 
# Temporary file is soon deleted. 
# 
if [[ ! "`dirname $0`" =~ ^/tmp/.sh-tmp ]]; then 
    mkdir -p /tmp/.sh-tmp/ 
    DIST="/tmp/.sh-tmp/$(basename $0)" 
    install -m 700 "$0" $DIST 
    exec $DIST "[email protected]" 
else 
    rm "$0" 
fi 

用例:保存/home/foo/a.sh此文件:如果你調用上面的腳本爲./a.sh 1 2 3

#!/bin/sh 
echo "$0 [email protected]" 
. /home/foo/the-above-code.sh 
echo "Again, $0 [email protected]" 

,結果是:

/home/foo/a.sh 1 2 3 
/tmp/.sh-tmp/a.sh 1 2 3 
Again, /tmp/.sh-tmp/a.sh 1 2 3 

這可能是最好的,但使用風險自負。

這也是衆所周知的,把所有的大括號,最後exit做的工作,如解釋here

1

剛剛添加該解決方案,以保護被更改運行BASH腳本一個簡單而乾淨的方式:

#! /bin/bash 
{ 
your_code; 
exit; 
} 
+0

在teika kazura的回答結束時也提到了,但仍然很方便地突出它我認爲。 – phils