Emacs工作流在运行时编辑Bash脚本

Mic*_*man 8 emacs bash

如果在脚本运行时编辑脚本, Bash可能会产生意外结果.但是,能够编辑我在shell中运行的脚本的临时副本通常会非常方便,但在再次运行它之前确保它回到原始位置.是否有人建议工作流程或自定义Emacs以促进这一点?

Gil*_*il' 8

M-x delete-file,按下down以插入获取您正在编辑的文件的路径,并RET删除该文件.当您下次保存缓冲区时,这将创建一个新文件.Bash将继续执行旧的已删除文件.

顺便说一下,在大多数情况下,您甚至不需要采取这种预防措施.Emacs通常会保存到临时文件,然后将该临时文件重命名为正确的文件名(删除旧文件,除非将其保留为备份).如果文件检测到无法正确重新创建文件,则Emacs仅覆盖文件:如果文件具有硬链接,或者您对目录没有写入权限.(我正在简化一些;详细信息在basic-save-buffer-2函数中files.el.)只要文件有一个目录条目,并且您对包含脚本的目录具有写权限,只需按C-x C-s.


tei*_*ura 7

在这个回复中,我

  1. 吉尔斯用理论补充了很好的答案.
  2. 建议使用emacs改进.
  3. 建议替代方案,纯粹通过shell脚本编写.

警告:我不是专业的程序员.

1理论

缺失的原因是安全的,但改变并非是在Unix中,已删除的文件从文件系统得到人迹罕至,但已经打开的文件的过程(在这里,/斌/庆典)仍然可以读取它,解释在这里.这不是bash做一些特别的事情,比如缓冲整个文件,但它只是读取.(即使在删除之后,您也可以在脚本运行时恢复脚本,如此处所述.在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)
Run Code Online (Sandbox Code Playgroud)

但这并不完美.它保存了文件模式,但就是这样.

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 "$@"
else
    rm "$0"
fi
Run Code Online (Sandbox Code Playgroud)

用法示例:将此文件保存在/home/foo/a.sh中:

#!/bin/sh
echo "$0 $@"
. /home/foo/the-above-code.sh
echo "Again, $0 $@"
Run Code Online (Sandbox Code Playgroud)

如果您调用上面的脚本./a.sh 1 2 3,结果是:

/home/foo/a.sh 1 2 3
/tmp/.sh-tmp/a.sh 1 2 3
Again, /tmp/.sh-tmp/a.sh 1 2 3
Run Code Online (Sandbox Code Playgroud)

这可能是最好的,但使用风险自负.

它也知道,把所有的梅开二度,以缔结exit做这项工作,为解释在这里.