-
Notifications
You must be signed in to change notification settings - Fork 124
/
shell-script-funcs.el
118 lines (88 loc) · 5.45 KB
/
shell-script-funcs.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
;;; shell-script-funcs --- Utility functions for shell script conversions
;;; Commentary:
;; A collection of functions helpful in attempting to translate shell
;; scripts into Elisp scripts.
(require 'em-glob)
;;; Code:
(defun ha/substring-replace (old-str new-str beg end)
"Return a new string where a subsection of OLD-STR has been replaced with NEW-STR beginning at position BEG and ending at END."
(concat (substring old-str 0 beg) new-str (substring old-str end)))
(defun ha/getvar (var-name)
"Return value of a variable or environment variable specified by VAR-NAME."
(or (getenv var-name) (eval (read var-name))))
(defun ha/substr-variables (str)
"Replace shell-like '$VAR' and '${variables}' in STR with the equivalent environment variables or Elisp variables. For instance: $HOME/.emacs.d could return /home/howard/.emacs.d -- Keep in mind that this is just a string, it does not do any validation to see if any files exist."
;; This function recursively calls this function with more and more
;; embedded variables substituted out, until no more variables are
;; found, and then it returns the results.
;;
;; Begin by checking to see if the string starts with ~ ...
(if (string-prefix-p "~/" str)
(ha/substr-variables
(concat (getenv "HOME") (substring str 1)))
;; Variables can either be simple $BLAH or ${some-larger}...
(let ((s (or (string-match "${\\([^ }]*\\)}" str)
(string-match "$\\([A-z_]*\\)" str)))
(e (match-end 0)))
(if (not s) ; No $ matches?
str ; Then just return the string.
(ha/substr-variables ; Recursively call with first var sub'd
(ha/substring-replace str (ha/getvar (match-string 1 str)) s e))))))
;; (ha/substr-variables "$HOME/.emacs.d/elisp/*.el")
;; (ha/substr-variables "~/.emacs.d/elisp/*.el")
;; (ha/substr-variables "${user-emacs-directory}elisp/*.el")
(defun ha/get-files (path &optional full)
"Return list of files that match the glob pattern, PATH. Allowing shell-like variable substitution from the environment, like $HOME, or from variables defined by `setq'. If FULL is specified, return absolute pathnames for each file."
(let ((subbed-path (ha/substr-variables path)))
(condition-case nil
(directory-files (file-name-directory subbed-path)
full
(eshell-glob-regexp
(file-name-nondirectory subbed-path)))
(error '()))))
;; (ha/get-files "$HOME/.emacs.d/elisp/*.el")
;; => ("init-blog.el" "init-client.el" "init-clojure.el" ...)
;; (ha/get-files "$HOME/.emacs.d/elisp/*.el" t)
;; => ("/home/howard/.emacs.d/elisp/init-blog.el" "/home/howard/.emacs.d/elisp/init-client.el" ...)
;; (ha/get-files "${user-emacs-directory}/elisp/*.el")
;; => ("init-blog.el" "init-client.el" "init-clojure.el" ...)
;; (ha/get-files "/foo/bar/*") => nil
(defun ha/get-path (path &rest extra)
"Return a file specification based on PATH. We should expand this function so that glob patterns work when specifying the parent, but shouldn't worry about matching any particular file. All EXTRA parameters are appended separated with / characters."
(let ((file-parts (cons (ha/substr-variables path) extra)))
(mapconcat 'identity file-parts "/")))
;; (ha/get-path "/home/bar") ;=> "/home/bar"
;; (ha/get-path "$HOME/.emacs.d/elisp/*.el") ;=> "/home/howard/.emacs.d/elisp/*.el"
;; (ha/get-path "/foo/bar" "baz")
;;
;; (let ((blah "blah-blah") )
;; (ha/get-path "/home/${blah}/flubber")) ;=> /home/blah-blah/flubber
;;
;; (let ((blah "blah-blah") )
;; (ha/get-path "/home/flubber" blah)) ;=> /home/flubber/blah-blah
;; ----------------------------------------------------------------------
;; The following functions are basic "shell" like functions that take
;; a path that refers to files. This allows us to not have to call
;; (ha/get-files) directly.
(defun ha/mkdir (path)
"Create a directory specified by PATH, which can contain embedded environment variables and Emacs variables, e.g. '$HOME/Work/foobar'."
(make-directory (ha/get-path path) t))
(defun ha/mksymlink (orig link)
"Create symbolic line to ORIG. If LINK is an existing link, it is deleted first. LINK could also refer to a directory. Note: Both parameters are strings that can accept embedded environment and Lisp variables, e.g. '$HOME/Work/foo.el' and '${user-emacs-directory}/elisp/bar.el' ."
(let ((orig-file (ha/get-path orig))
(link-file (ha/get-path link)))
(if (not (file-symlink-p link-file))
(make-symbolic-link orig-file link-file t))))
(defun ha/mksymlinks (files dest)
"Create an absolute symbolic link for each file specified in FILES into the directory, DEST (where each link will have the same name). Both parameters can be specified with glob patterns and embedded environment and Emacs variables, e.g. '$HOME/.emacs.d/*.el'."
(mapc (lambda (file) (ha/mksymlink file dest)) (ha/get-files files t)))
(defun ha/mkdirs (path)
"Create one or more directories specified by PATH, which can contain embedded environment variables and Emacs variables, e.g. '$HOME/Work/foobar'."
(mapc (lambda (dir) (make-directory dir t)) (ha/get-files path)))
(defun ha/copy-files (from to files)
"Copy some files FROM a directory TO another directory, where FILES is a list of names."
(mapcar (lambda (file)
(copy-file (ha/get-path from file)
(ha/get-path to) t)) files))
(provide 'shell-script-funcs)
;;; shell-script-funcs ends here