Bash可以提供unexpected results if one edits a script while the script is running。但是,能够编辑我在shell中运行的脚本的临时副本通常会非常方便,但在再次运行它之前确保它回到原始位置。有没有人对Emacs的工作流程或自定义有什么建议来促进这个?
答案 0 :(得分:8)
M-x delete-file
,按down
插入获取您正在编辑的文件的路径,然后按RET
删除该文件。当您下次保存缓冲区时,这将创建一个新文件。 Bash将继续执行旧的已删除文件。
顺便说一句,在大多数情况下,您甚至不需要采取这种预防措施。 Emacs通常会保存到临时文件,然后将该临时文件重命名为正确的文件名(删除旧文件,除非将其保留为备份)。如果文件检测到无法正确重新创建文件,则Emacs仅覆盖文件:如果文件具有硬链接,或者您对目录没有写入权限。 (我简化了一点;详细信息在basic-save-buffer-2
中的files.el
函数中。)只要文件只有一个目录条目,并且您对包含脚本的目录有写权限,按C-x C-s
。
答案 1 :(得分:7)
在这个回复中,我
警告:我不是专业程序员。
1理论
删除的原因是安全的,但修改不是在Unix中,删除的文件无法从文件系统访问,但打开文件的进程(此处为/ bin / bash)仍然可以读取它,如解释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 "$@"
else
rm "$0"
fi
用法示例:将此文件保存在/home/foo/a.sh中:
#!/bin/sh
echo "$0 $@"
. /home/foo/the-above-code.sh
echo "Again, $0 $@"
如果您将上述脚本调用为./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
这可能是最好的,但使用风险自负。
众所周知,按照here的说法,将所有内容放在大括号中,以exit
结束即可完成工作。
答案 2 :(得分: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)))
答案 3 :(得分:2)
只需添加此解决方案,即可轻松,简洁地保护正在运行的BASH脚本不被更改:
#! /bin/bash
{
your_code;
exit;
}