emacs에서 새 파일을 생성하는 동안 존재하지 않는 디렉토리를 생성 할 수 있습니까?


29

emacs에서는 Cx Cf로 파일을 방문하여 파일을 만듭니다. 내가 만들고 싶다고 가정 해 봅시다 /home/myself/new_directory/file.txt.

new_directory가 아직 file.txt없으면 추가 단계없이 작성하는 동안 작성하는 방법이 있습니까? (저는 Linux 에서 -p플래그를 사용하는 것과 같은 것을 생각하고 mkdir있습니다.)

이것을 할 수있는 Cx Cf 대신 다른 키 입력이있는 것처럼 느껴지지만 그것이 무엇인지 기억할 수는 없습니다.


emacs에는 편집하는 동안 명령을 지연시키는 것이 없습니다 ?? vi에서 가능:!mkdir -p ~/new_dir/to/some/crazy/path
DaveParillo

3
@DaveParillo : 물론 있습니다 M-!.
Nikana Reklawyks

prelude ( github.com/bbatsov/prelude ) 플러그인을 사용합니다. 위와 같은 파일을 만들 때마다 "Create directory ..."라는 메시지가 표시됩니다. 간단히 "y"를 선택할 수 있습니다. 그런 다음 "파일이 없습니다. 작성 하시겠습니까? (y 또는 n)"입니다. y를 선택하면 새 파일이 만들어집니다. 파일을 저장하면 위의 정보로 파일이 생성됩니다.
Pramod

답변:


25

find-file필요한 디렉토리를 투명하게 작성하는 기능 을 조언 할 수도 있습니다 .

(defadvice find-file (before make-directory-maybe (filename &optional wildcards) activate)
  "Create parent directory if not exists while visiting file."
  (unless (file-exists-p filename)
    (let ((dir (file-name-directory filename)))
      (unless (file-exists-p dir)
        (make-directory dir)))))

간단히 .emacs어딘가에 넣고 C-x C-f평소처럼 사용 하십시오 .


2
훌륭한 해결책. 나는 작은 것들을 개선하는 것이 어떻게 이멕스가 더 나은 일을 할 수 있도록 완전히 새로운 힌트의 세계를 열 수 있는지를 좋아합니다 (예, 나는 defadvice전에는 몰랐습니다 ).
Nikana Reklawyks

18

존재하지 않는 구성 요소 find-file(예 : Cx Cf )가 있는 경로 이름을 제공하면 추가 메시지가 표시됩니다.

Mx make-directory RET RET을 사용하여 디렉토리 및 해당 상위를 작성하십시오.

버퍼를 처음 저장할 때까지 파일이 작성되지 않으므로 make-directory새 버퍼가 나타난 직후에 실행 하거나 파일을 저장하기 전에 다른 시간에 수행 할 수 있습니다. 그런 다음 디렉토리가 필요한 버퍼에서 Mx make-directory RET RET 을 입력하십시오 (디렉토리를 작성하도록 프롬프트합니다 (기본값은 버퍼의 경로 이름에서 파생 됨). 두 번째 RET은 기본값을 승인 함).


14

이도 모드를 제공한다 ido-find-file즉의 대체 find-file당신에게 훨씬 더 많은 기능을 제공합니다. 예를 들어 파일을 여는 동안 새 디렉토리를 만들 수 있습니다.

  • C-x C-f평소와 같이 입력 합니다 (에 다시 매핑 ido-find-file).

  • 존재하지 않는 경로를 제공하고

  • 를 누르면 M-m새 디렉토리를 만들라는 메시지가 나타납니다.

  • 그런 다음 새로 만든 디렉토리에서 방문 할 파일 이름을 지정하십시오.


나는 그것을 얻지 못한다 : 왜 M-m어떤 시점에서 언론이 왜 C-x C-f모든 것이 자동적으로 만들어지지 않는다면? 디렉토리를 만들 수를 묻는 메시지가되는 경우 M-! mkdir나 ... 너무 공정한 작업을 할 것입니다 dired
Nikana Reklawyks을

ido-find-file디렉토리를 자동으로 생성 하는 방법이 있습니까? 아니면 더 좋게 만들 것인지 물어보십시오. 나는 Török Gábor의 답변에서 조언을 사용해 보았지만 어떤 기능을 적용 할 것인지 알 수 없었습니다 (ido가 find-file직접 전화하지 않기 때문에)
Troy Daniels

1

내 솔루션에는 보너스가 있습니다. 버퍼를 저장하지 않고 죽이면 Emacs는 생성 된 빈 디렉토리를 삭제하도록 제안합니다 (그러나 호출하기 전에 존재하지 않는 경우에만 find-file).

;; Automatically create any nonexistent parent directories when
;; finding a file. If the buffer for the new file is killed without
;; being saved, then offer to delete the created directory or
;; directories.

(defun radian--advice-find-file-automatically-create-directory
    (original-function filename &rest args)
  "Automatically create and delete parent directories of files.
This is an `:override' advice for `find-file' and friends. It
automatically creates the parent directory (or directories) of
the file being visited, if necessary. It also sets a buffer-local
variable so that the user will be prompted to delete the newly
created directories if they kill the buffer without saving it."
  ;; The variable `dirs-to-delete' is a list of the directories that
  ;; will be automatically created by `make-directory'. We will want
  ;; to offer to delete these directories if the user kills the buffer
  ;; without saving it.
  (let ((dirs-to-delete ()))
    ;; If the file already exists, we don't need to worry about
    ;; creating any directories.
    (unless (file-exists-p filename)
      ;; It's easy to figure out how to invoke `make-directory',
      ;; because it will automatically create all parent directories.
      ;; We just need to ask for the directory immediately containing
      ;; the file to be created.
      (let* ((dir-to-create (file-name-directory filename))
             ;; However, to find the exact set of directories that
             ;; might need to be deleted afterward, we need to iterate
             ;; upward through the directory tree until we find a
             ;; directory that already exists, starting at the
             ;; directory containing the new file.
             (current-dir dir-to-create))
        ;; If the directory containing the new file already exists,
        ;; nothing needs to be created, and therefore nothing needs to
        ;; be destroyed, either.
        (while (not (file-exists-p current-dir))
          ;; Otherwise, we'll add that directory onto the list of
          ;; directories that are going to be created.
          (push current-dir dirs-to-delete)
          ;; Now we iterate upwards one directory. The
          ;; `directory-file-name' function removes the trailing slash
          ;; of the current directory, so that it is viewed as a file,
          ;; and then the `file-name-directory' function returns the
          ;; directory component in that path (which means the parent
          ;; directory).
          (setq current-dir (file-name-directory
                             (directory-file-name current-dir))))
        ;; Only bother trying to create a directory if one does not
        ;; already exist.
        (unless (file-exists-p dir-to-create)
          ;; Make the necessary directory and its parents.
          (make-directory dir-to-create 'parents))))
    ;; Call the original `find-file', now that the directory
    ;; containing the file to found exists. We make sure to preserve
    ;; the return value, so as not to mess up any commands relying on
    ;; it.
    (prog1 (apply original-function filename args)
      ;; If there are directories we want to offer to delete later, we
      ;; have more to do.
      (when dirs-to-delete
        ;; Since we already called `find-file', we're now in the buffer
        ;; for the new file. That means we can transfer the list of
        ;; directories to possibly delete later into a buffer-local
        ;; variable. But we pushed new entries onto the beginning of
        ;; `dirs-to-delete', so now we have to reverse it (in order to
        ;; later offer to delete directories from innermost to
        ;; outermost).
        (setq-local radian--dirs-to-delete (reverse dirs-to-delete))
        ;; Now we add a buffer-local hook to offer to delete those
        ;; directories when the buffer is killed, but only if it's
        ;; appropriate to do so (for instance, only if the directories
        ;; still exist and the file still doesn't exist).
        (add-hook 'kill-buffer-hook
                  #'radian--kill-buffer-delete-directory-if-appropriate
                  'append 'local)
        ;; The above hook removes itself when it is run, but that will
        ;; only happen when the buffer is killed (which might never
        ;; happen). Just for cleanliness, we automatically remove it
        ;; when the buffer is saved. This hook also removes itself when
        ;; run, in addition to removing the above hook.
        (add-hook 'after-save-hook
                  #'radian--remove-kill-buffer-delete-directory-hook
                  'append 'local)))))

;; Add the advice that we just defined.
(advice-add #'find-file :around
            #'radian--advice-find-file-automatically-create-directory)

;; Also enable it for `find-alternate-file' (C-x C-v).
(advice-add #'find-alternate-file :around
            #'radian--advice-find-file-automatically-create-directory)

;; Also enable it for `write-file' (C-x C-w).
(advice-add #'write-file :around
            #'radian--advice-find-file-automatically-create-directory)

(defun radian--kill-buffer-delete-directory-if-appropriate ()
  "Delete parent directories if appropriate.
This is a function for `kill-buffer-hook'. If
`radian--advice-find-file-automatically-create-directory' created
the directory containing the file for the current buffer
automatically, then offer to delete it. Otherwise, do nothing.
Also clean up related hooks."
  (when (and
         ;; Stop if there aren't any directories to delete (shouldn't
         ;; happen).
         radian--dirs-to-delete
         ;; Stop if `radian--dirs-to-delete' somehow got set to
         ;; something other than a list (shouldn't happen).
         (listp radian--dirs-to-delete)
         ;; Stop if the current buffer doesn't represent a
         ;; file (shouldn't happen).
         buffer-file-name
         ;; Stop if the buffer has been saved, so that the file
         ;; actually exists now. This might happen if the buffer were
         ;; saved without `after-save-hook' running, or if the
         ;; `find-file'-like function called was `write-file'.
         (not (file-exists-p buffer-file-name)))
    (cl-dolist (dir-to-delete radian--dirs-to-delete)
      ;; Ignore any directories that no longer exist or are malformed.
      ;; We don't return immediately if there's a nonexistent
      ;; directory, because it might still be useful to offer to
      ;; delete other (parent) directories that should be deleted. But
      ;; this is an edge case.
      (when (and (stringp dir-to-delete)
                 (file-exists-p dir-to-delete))
        ;; Only delete a directory if the user is OK with it.
        (if (y-or-n-p (format "Also delete directory `%s'? "
                              ;; The `directory-file-name' function
                              ;; removes the trailing slash.
                              (directory-file-name dir-to-delete)))
            (delete-directory dir-to-delete)
          ;; If the user doesn't want to delete a directory, then they
          ;; obviously don't want to delete any of its parent
          ;; directories, either.
          (cl-return)))))
  ;; It shouldn't be necessary to remove this hook, since the buffer
  ;; is getting killed anyway, but just in case...
  (radian--remove-kill-buffer-delete-directory-hook))

(defun radian--remove-kill-buffer-delete-directory-hook ()
  "Clean up directory-deletion hooks, if necessary.
This is a function for `after-save-hook'. Remove
`radian--kill-buffer-delete-directory-if-appropriate' from
`kill-buffer-hook', and also remove this function from
`after-save-hook'."
  (remove-hook 'kill-buffer-hook
               #'radian--kill-buffer-delete-directory-if-appropriate
               'local)
  (remove-hook 'after-save-hook
               #'radian--remove-kill-buffer-delete-directory-hook
               'local))

정식 버전은 여기 입니다.


0

MCh make-directory에 대한 @Chris의 제안 외에도 find-file과 make-directory를 수행하는 짧은 elisp 함수를 작성할 수 있습니다.

(defun bp-find-file(filename &optional wildcards)
  "finds a file, and then creates the folder if it doesn't exist"

  (interactive (find-file-read-args "Find file: " nil))
  (let ((value (find-file-noselect filename nil nil wildcards)))
    (if (listp value)
    (mapcar 'switch-to-buffer (nreverse value))
      (switch-to-buffer value)))
  (when (not (file-exists-p default-directory))
       (message (format "Creating  %s" default-directory))
       (make-directory default-directory t))
  )

예쁘지 않고 "Createing directory ..."라고 말하기 전에 "it MX make-directory ...."를 깜빡이지만 다른 것이 없다면 시작해야합니다.


2
이 접근법의 경우, 직접 find-file호출하는 다른 함수 find-file가 수정 된 동작의 이점을 얻을 수 있도록 새 함수를 정의하는 대신 원래 함수 를 조언하는 것이 좋습니다 .
Török Gábor

그러나 find-file은 이것을 수행 할 수있는 어떤 주장도하지 않는 것 같습니다 ... find-file-hooks에서 할 수있는 유용한 것이 없다면 ...
Brian Postow

나는 이것을 의미했다 : superuser.com/questions/131538/…
Török Gábor

0
(make-directory "~/bunch/of/dirs" t)

디렉토리가 이미 존재하면 경고가 표시됩니다.

( C-h f make-directory RET) 매뉴얼에서 :

make-directory는 대화식으로 컴파일 된 Lisp 함수입니다.

(make-directory DIR &optional PARENTS)

디렉토리 DIR및 선택적으로 존재하지 않는 상위 디렉토리를 작성하십시오 . 경우 DIR이미 디렉토리로 존재하지 않는 한, 오류 신호 PARENTS가 아닌 무기 호입니다.

대화식으로 만들 디렉토리의 기본 선택은 현재 버퍼의 기본 디렉토리입니다. 존재하지 않는 디렉토리에있는 파일을 방문했을 때 유용합니다.

비 대화식으로, 두 번째 (선택적) 인수 PARENTS는 nil이 아닌 경우 존재하지 않는 부모 디렉토리를 만들지 여부를 나타냅니다. 대화식으로 이것은 기본적으로 발생합니다.

디렉토리 작성에 실패하면 오류가 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.