@table @code
@item if @var{conditional} @var{true-subcommand}
-@itemx if @var{conditional} @var{true-subcommand} @var{false-subcommand}
+@itemx if @var{conditional} @var{true-subcommand} else @var{false-subcommand}
Evaluate @var{true-subcommand} if @var{conditional} is satisfied;
otherwise, evaluate @var{false-subcommand}. Both @var{true-subcommand}
and @var{false-subcommand} should be subcommands, as with
@var{conditional}.
+You can also chain together @code{if}/@code{else} forms, for example:
+
+@example
+if @{[ -f file.txt ]@} @{
+ echo found file
+@} else if @{[ -f alternate.txt ]@} @{
+ echo found alternate
+@} else @{
+ echo not found!
+@}
+@end example
+
@item unless @var{conditional} @var{false-subcommand}
-@itemx unless @var{conditional} @var{false-subcommand} @var{true-subcommand}
+@itemx unless @var{conditional} @var{false-subcommand} else @var{true-subcommand}
Evaluate @var{false-subcommand} if @var{conditional} is not satisfied;
-otherwise, evaluate @var{true-subcommand}.
+otherwise, evaluate @var{true-subcommand}. Like above, you can also
+chain together @code{unless}/@code{else} forms.
@item while @var{conditional} @var{subcommand}
Repeatedly evaluate @var{subcommand} so long as @var{conditional} is
where to send the standard error output. See the "(eshell) Entry
Points" node in the Eshell manual for more details.
++++
+*** Conditional statements in Eshell now use an 'else' keyword.
+Eshell now prefers the following form when writing conditionals:
+
+ if {conditional} {true-subcommand} else {false-subcommand}
+
+The old form (without the 'else' keyword) is retained for compatibility.
+
++++
+*** You can now chain conditional statements in Eshell.
+When using the newly-preferred conditional form in Eshell, you can now
+chain together multiple 'if'/'else' statements. For more information,
+see "(eshell) Control Flow" in the Eshell manual.
+
+++
*** Eshell's built-in 'wait' command now accepts a timeout.
By passing '-t' or '--timeout', you can specify a maximum time to wait
,body)
(setq ,for-items (cdr ,for-items)))))))
-(defun eshell-structure-basic-command (func names keyword test body
- &optional else)
+(defun eshell-structure-basic-command (func names keyword test &rest body)
"With TERMS, KEYWORD, and two NAMES, structure a basic command.
The first of NAMES should be the positive form, and the second the
negative. It's not likely that users should ever need to call this
function."
+ (unless test
+ (error "Missing test for `%s' command" keyword))
+
;; If the test form is a subcommand, wrap it in `eshell-commands' to
;; silence the output.
(when (memq (car test) '(eshell-as-subcommand eshell-lisp-command))
(setq test `(not ,test)))
;; Finally, create the form that represents this structured command.
- `(,func ,test ,body ,else))
+ `(,func ,test ,@body))
(defun eshell-rewrite-while-command (terms)
"Rewrite a `while' command into its equivalent Eshell command form.
Because the implementation of `while' relies upon conditional
evaluation of its argument (i.e., use of a Lisp special form), it
must be implemented via rewriting, rather than as a function."
- (if (and (stringp (car terms))
- (member (car terms) '("while" "until")))
- (eshell-structure-basic-command
- 'while '("while" "until") (car terms)
- (cadr terms)
- (car (last terms)))))
+ (when (and (stringp (car terms))
+ (member (car terms) '("while" "until")))
+ (eshell-structure-basic-command
+ 'while '("while" "until") (car terms)
+ (cadr terms)
+ (caddr terms))))
(defun eshell-rewrite-if-command (terms)
"Rewrite an `if' command into its equivalent Eshell command form.
Because the implementation of `if' relies upon conditional
evaluation of its argument (i.e., use of a Lisp special form), it
must be implemented via rewriting, rather than as a function."
- (if (and (stringp (car terms))
- (member (car terms) '("if" "unless")))
- (eshell-structure-basic-command
- 'if '("if" "unless") (car terms)
- (cadr terms)
- (car (last terms (if (= (length terms) 4) 2)))
- (when (= (length terms) 4)
- (car (last terms))))))
+ (when (and (stringp (car terms))
+ (member (car terms) '("if" "unless")))
+ (eshell-structure-basic-command
+ 'if '("if" "unless") (car terms)
+ (cadr terms)
+ (caddr terms)
+ (if (equal (nth 3 terms) "else")
+ ;; If there's an "else" keyword, allow chaining together
+ ;; multiple "if" forms...
+ (or (eshell-rewrite-if-command (nthcdr 4 terms))
+ (nth 4 terms))
+ ;; ... otherwise, only allow a single "else" block (without the
+ ;; keyword) as before for compatibility.
+ (nth 3 terms)))))
(defun eshell-set-exit-info (status &optional result)
"Set the exit status and result for the last command.
(ert-deftest esh-cmd-test/if-else-statement ()
"Test invocation of an if/else statement."
(let ((eshell-test-value t))
- (eshell-command-result-equal "if $eshell-test-value {echo yes} {echo no}"
- "yes"))
+ (eshell-command-result-equal
+ "if $eshell-test-value {echo yes} {echo no}" "yes")
+ (eshell-command-result-equal
+ "if $eshell-test-value {echo yes} else {echo no}" "yes"))
(let ((eshell-test-value nil))
- (eshell-command-result-equal "if $eshell-test-value {echo yes} {echo no}"
- "no")))
+ (eshell-command-result-equal
+ "if $eshell-test-value {echo yes} {echo no}" "no")
+ (eshell-command-result-equal
+ "if $eshell-test-value {echo yes} else {echo no}" "no")))
(ert-deftest esh-cmd-test/if-else-statement-lisp-form ()
"Test invocation of an if/else statement using a Lisp form."
(eshell-command-result-equal "if {[ foo = bar ]} {echo yes} {echo no}"
"no"))
+(ert-deftest esh-cmd-test/if-else-statement-chain ()
+ "Test invocation of a chained if/else statement."
+ (dolist (case '((1 . "one") (2 . "two") (3 . "other")))
+ (let ((eshell-test-value (car case)))
+ (eshell-command-result-equal
+ (concat "if (= eshell-test-value 1) {echo one} "
+ "else if (= eshell-test-value 2) {echo two} "
+ "else {echo other}")
+ (cdr case)))))
+
(ert-deftest esh-cmd-test/if-statement-pipe ()
"Test invocation of an if statement piped to another command."
(skip-unless (executable-find "rev"))