2011-12-01 63 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