From fac640ee8608748f0c4b867080d554c1b94121bd Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Mon, 18 Apr 2022 12:58:54 +0200 Subject: [PATCH] Make it easier to use Emacs as a script interpreter * doc/emacs/cmdargs.texi (Initial Options): Document -x. * lisp/startup.el (command-line-1): Add new -scripteval. (command-line--eval-script): New function. * src/emacs.c (main): Transform -x to -scripteval. (standard_args): Add -x (bug#20682). --- doc/emacs/cmdargs.texi | 16 ++++++++++++++++ etc/NEWS | 11 +++++++++++ lisp/startup.el | 23 +++++++++++++++++++++-- src/emacs.c | 16 +++++++++++++++- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi index de1d5e0b2c3..946afb6fc14 100644 --- a/doc/emacs/cmdargs.texi +++ b/doc/emacs/cmdargs.texi @@ -294,6 +294,22 @@ which will invoke Emacs with @samp{--script} and supply the name of the script file as @var{file}. Emacs Lisp then treats the @samp{#!} on this first line as a comment delimiter. +@item -x +@opindex -x +This option can only be used in executable script files, and should be +invoked like this: + +@example +#!/usr/bin/emacs -x +@end example + +This is like @samp{--script}, but suppresses loading the init files +(like @code{--quick}), and can't be used on a normal command line +(since it doesn't specify the script to load). In addition, when it +reaches the end of the script, it exits Emacs and uses the value of +the final form as the exit value from the script (if the final value +is numerical). Otherwise, it will always exit with a zero value. + @item --no-build-details @opindex --no-build-details @cindex build details diff --git a/etc/NEWS b/etc/NEWS index ec56839c064..3e7788277d3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -100,6 +100,17 @@ Saving Emacs Sessions" node in the Emacs manual for more details. * Startup Changes in Emacs 29.1 ++++ +** Emacs can now be used more easily in an executable script. +If you start an executable script with + + #!/usr/bin/emacs -x + +Emac will start without reading any init files (like with --quick), +and then execute the rest of the script file as Emacs Lisp. When it +reaches the end of the script, Emacs will exit with an exit code from +the value of the final form. + +++ ** Emacs now supports setting 'user-emacs-directory' via '--init-directory'. diff --git a/lisp/startup.el b/lisp/startup.el index ab7b81a707e..353b5c78f17 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -2664,7 +2664,7 @@ nil default-directory" name) ;; This is used to handle -script. It's not clear ;; we need to document it (it is totally internal). - ((member argi '("-scriptload")) + ((member argi '("-scriptload" "-scripteval")) (let* ((file (command-line-normalize-file-name (or argval (pop command-line-args-left)))) ;; Take file from default dir. @@ -2677,7 +2677,10 @@ nil default-directory" name) ;; actually exist on some systems. (when (file-exists-p truename) (setq file-ex truename)) - (command-line--load-script file-ex))) + (if (equal argi "-scripteval") + ;; This will kill Emacs. + (command-line--eval-script file-ex) + (command-line--load-script file-ex)))) ((equal argi "-insert") (setq inhibit-startup-screen t) @@ -2879,6 +2882,22 @@ nil default-directory" name) (delete-line)) (eval-buffer buffer nil file nil t))))) +(defun command-line--eval-script (file) + (load-with-code-conversion + file file nil t + (lambda (buffer _) + (with-current-buffer buffer + (goto-char (point-min)) + (when (looking-at "#!") + (forward-line)) + (let (value form) + (while (ignore-error 'end-of-file + (setq form (read (current-buffer)))) + (setq value (eval form t))) + (kill-emacs (if (numberp value) + value + 0))))))) + (defun command-line-normalize-file-name (file) "Collapse multiple slashes to one, to handle non-Emacs file names." (save-match-data diff --git a/src/emacs.c b/src/emacs.c index 2fb62cb69af..3100852b2c0 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -296,7 +296,10 @@ Initialization options:\n\ -q --no-site-file --no-site-lisp --no-splash\n\ --no-x-resources\n\ --script FILE run FILE as an Emacs Lisp script\n\ ---terminal, -t DEVICE use DEVICE for terminal I/O\n\ +-x to be used in #!/usr/bin/emacs -x\n\ + and has approximately the same meaning\n\ + as -Q --script\n\ +--terminal, -t DEVICE use DEVICE for terminal I/O\n \ --user, -u USER load ~USER/.emacs instead of your own\n\ \n\ ", @@ -2063,6 +2066,16 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem no_site_lisp = 1; } + if (argmatch (argv, argc, "-x", 0, 1, &junk, &skip_args)) + { + noninteractive = 1; + no_site_lisp = 1; + /* This is picked up in startup.el. */ + argv[skip_args - 1] = (char *) "-scripteval"; + skip_args -= 1; + sort_args (argc, argv); + } + /* Don't actually discard this arg. */ skip_args = count_before; } @@ -2504,6 +2517,7 @@ static const struct standard_args standard_args[] = /* (Note that to imply -nsl, -Q is partially handled here.) */ { "-Q", "--quick", 55, 0 }, { "-quick", 0, 55, 0 }, + { "-x", 0, 55, 0 }, { "-q", "--no-init-file", 50, 0 }, { "-no-init-file", 0, 50, 0 }, { "-init-directory", "--init-directory", 30, 1 }, -- 2.39.2