2011-12-01 30 views
7

Bash có thể cung cấp unexpected results if one edits a script while the script is running. Nhưng thường rất thuận tiện để có thể chỉnh sửa bản sao tạm thời của tập lệnh mà tôi chạy trong một trình bao, nhưng hãy đảm bảo rằng nó đã trở lại vị trí ban đầu trước khi tôi chạy lại. Có ai có đề xuất cho một công việc hoặc các tùy chỉnh để Emacs để tạo điều kiện này?Quy trình làm việc của Emacs để chỉnh sửa tập lệnh Bash trong khi chúng chạy

Trả lời

8

M-x delete-file, nhấn down để chèn được đường dẫn đến tập tin bạn đang chỉnh sửa, và RET để xóa các tập tin. Khi bạn tiếp tục lưu bộ đệm của bạn, điều này sẽ tạo ra một tập tin mới. Bash sẽ tiếp tục thực thi tệp cũ, đã bị xóa.

Nhân tiện, trong hầu hết các trường hợp, bạn thậm chí không cần thực hiện biện pháp phòng ngừa này. Emacs thường lưu vào một tệp tạm thời, sau đó đổi tên tệp tạm thời đó thành tên tệp thích hợp (xóa tệp cũ, trừ khi nó được giữ như một bản sao lưu). Emacs chỉ ghi đè lên tập tin tại chỗ nếu nó phát hiện ra rằng nó không thể tái tạo các tập tin đúng: nếu tập tin có liên kết cứng, hoặc nếu bạn không có quyền ghi vào thư mục. (Tôi đơn giản hóa một chút, các chi tiết nằm trong hàm basic-save-buffer-2 trong files.el.) Miễn là tệp có một mục nhập thư mục duy nhất và bạn có quyền ghi vào thư mục chứa tập lệnh, chỉ cần bấm C-x C-s.

+0

That sounds really đơn giản. Tôi cần phải kiểm tra kỹ xem điều này có thực sự hoạt động không. Nếu vậy, điều đó có nghĩa là hành vi lưu và đổi tên hiện tại của Emacs sẽ giữ cho vấn đề này không bị xén không? –

+0

@MichaelHoffman Có, nếu Emacs đang thực hiện lưu và đổi tên (đây là hành vi mặc định trong hầu hết các trường hợp) thì bạn sẽ không gặp vấn đề gì cả. Lưu ý rằng tôi giả sử một unix thực sự ở đây, nếu bạn đang chạy bash trên một cái gì đó như Cygwin bạn có thể không có khả năng loại bỏ các tập tin (Tôi không chắc chắn về điều này). – Gilles

+0

Bạn có thể chỉnh sửa câu trả lời của mình để bao gồm câu trả lời này không? Có vẻ như toàn bộ câu hỏi là không cần thiết, bởi vì Emacs làm điều đúng đắn từ chiếc hộp. –

2

tôi đã có chức năng đổi tên tệp bộ đệm hiện tại. là một bước nhảy ngắn đối với một chức năng giúp dễ dàng chuyển sang phiên bản tạm thời của một tệp và sau đó quay lại. về cơ bản, nếu bạn chạy hàm này khi truy cập một tệp thông thường "foo.txt", nó sẽ sao chép tệp vào một tệp mới trong cùng một thư mục có tên "tmp.foo.txt". chỉnh sửa như mong muốn. khi đã sẵn sàng để chuyển trở lại, hãy chạy lại hàm và nó sẽ di chuyển tệp tạm thời trở lại trên tệp gốc. tôi đã sử dụng tiền tố "tmp." thay vì hậu tố bởi vì hầu hết nhận dạng chế độ trong các emacs đều dựa trên hậu tố (bạn có thể làm điều gì đó huyền ảo hơn với việc nhớ tất cả các chế độ đệm và áp dụng lại ...).

(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

Trong thư trả lời này, tôi

  1. bổ sung câu trả lời thoải mái bởi Gilles với lý thuyết.
  2. đề xuất một sự cải thiện trong việc sử dụng biểu tượng cảm xúc.
  3. đề xuất các lựa chọn thay thế, hoàn toàn bằng cách viết kịch bản lệnh shell.

Cảnh báo: Tôi không phải là lập trình viên chuyên nghiệp.

1 Lý thuyết

Việc xóa lý do là an toàn nhưng thay đổi là không phải là trong Unix, một tập tin đã xóa được không thể tiếp cận từ hệ thống tập tin, nhưng quá trình đó đã mở file (ở đây,/bin/bash) vẫn có thể đọc nó, như được giải thích here. Nó không phải là bash làm một cái gì đó đặc biệt, giống như đệm toàn bộ tập tin, nhưng nó chỉ đơn giản là đọc. (Ngay cả sau khi xóa, bạn có thể phục hồi các tập lệnh trong khi nó đang chạy, as explained here Trong bash, kịch bản dường như luôn mở như 255, /proc/<pid>/fd/255..)

2 Emacs giải pháp

Bây giờ Emacs; để được an toàn, bạn có thể tự động hóa tất cả; thêm vào before-save-hook một chức năng để kiểm tra xem nó là sh-mode, và xóa tự động nếu kịch bản đang mở, bằng cách kiểm tra với 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) 

Nhưng điều này không phải là hoàn hảo. Nó lưu chế độ tập tin, nhưng đó là tất cả.

3 Giải pháp bash thuần túy

Hoặc, bạn có thể để chính kịch bản bash tự xóa. Sử dụng điều này:

# 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 

dụ Cách sử dụng: lưu tập tin này trong /home/foo/a.sh:

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

Nếu bạn gọi kịch bản trên như ./a.sh 1 2 3, kết quả là:

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

Điều này có thể là tốt nhất, nhưng tự chịu rủi ro.

Nó cũng được biết rằng đặt tất cả trong một cú đúp, kết thúc với exit thực hiện công việc, như được giải thích here.

1

Chỉ cần thêm giải pháp này như một cách dễ dàng và sạch sẽ để bảo vệ chạy script BASH khỏi bị thay đổi:

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

Được đề cập ở phần cuối của câu trả lời của teika kazura, nhưng vẫn tiện dụng để làm nổi bật nó tôi nghĩ. – phils

Các vấn đề liên quan