Hi,

Below is the patch for the new 'errfail' option.

Please note that this is MINIMAL implementation. It will cover the cases
below. Possible to think about this as replacing every ';' (or new line
line that terminate statements) with '&&' - forcing execution to break.
.
Patch is minimal (less than 15 lines of code changes), and is low-risk -
all changes are conditional on the (global) flag setting. No complex logic,
No write of functions.

Feedback/comments are welcomed.

set -o errfail
function foo { echo A ; false ; echo B ; }
function bar { echo C ; foo ; echo D ; }

 # Will print A, return non-zero status
foo

# Return from function on first error
# Will print A, CATCH
foo || { echo CATCH  ; }

# Failures will propagate through function calls, unless explicitly "caught"
# Print C A CATCH
bar || echo CATCH

# Fancier: "throw"
function throw { echo "${0##*/}: $@" >& 2 ; false ; }

function foo {
    if [ ! -f required-file.txt ] ; then
        throw "Missing required file"
    fi
    echo "YES"
}

Small Letters:
* The errfail does NOT cover "top-level" errors. Only "connected"
statements. Either create a 'main' function, or create a top level block:

On Tue, Jul 5, 2022 at 12:00 AM Lawrence Velázquez <v...@larryv.me> wrote:

> On Mon, Jul 4, 2022, at 3:55 PM, Yair Lenga wrote:
> > I'm sorry - I misunderstood your original comments. I'll prepare the
> > patched version (at least, I would like to add comments before
> > publishing...) , and share it.
> > Where/how can I post it ?
>
> Send it to this list as an attachment [1] with a .txt suffix [2].
>
> [1] Gmail will mangle the patch if you send it inline.
> [2] Alleviates issues with clients on the receiving end.
>
> > I did not see anyone else dropping source
> > code/patches into the group ?
>
> Code contributions are not as common as you might think, given
> bash's prominence.
>
> --
> vq
>
diff -ru orig/bash-master/builtins/set.def new/bash-master/builtins/set.def
--- orig/bash-master/builtins/set.def   2022-01-05 00:03:45.000000000 +0200
+++ new/bash-master/builtins/set.def    2022-07-05 11:54:31.545828400 +0300
@@ -76,6 +76,8 @@
           emacs        use an emacs-style line editing interface
 #endif /* READLINE */
           errexit      same as -e
+          errfail      execution of command lists will stop whenever
+                       a single command return non-zero status
           errtrace     same as -E
           functrace    same as -T
           hashall      same as -h
@@ -196,6 +198,7 @@
   { "emacs",     '\0', (int *)NULL, set_edit_mode, get_edit_mode },
 #endif
   { "errexit",   'e', (int *)NULL, (setopt_set_func_t *)NULL, 
(setopt_get_func_t *)NULL  },
+  { "errfail",   '\0', &errfail_opt, (setopt_set_func_t *)NULL, 
(setopt_get_func_t *)NULL  },
   { "errtrace",          'E', (int *)NULL, (setopt_set_func_t *)NULL, 
(setopt_get_func_t *)NULL  },
   { "functrace",  'T', (int *)NULL, (setopt_set_func_t *)NULL, 
(setopt_get_func_t *)NULL  },
   { "hashall",    'h', (int *)NULL, (setopt_set_func_t *)NULL, 
(setopt_get_func_t *)NULL  },
@@ -655,6 +658,7 @@
 {
   pipefail_opt = 0;
   ignoreeof = 0;
+/*  errfail_opt = 0 ;       errfail IS inherit by sub-shells */
 
 #if defined (STRICT_POSIX)
   posixly_correct = 1;
diff -ru orig/bash-master/execute_cmd.c new/bash-master/execute_cmd.c
--- orig/bash-master/execute_cmd.c      2022-01-05 00:03:45.000000000 +0200
+++ new/bash-master/execute_cmd.c       2022-07-05 13:03:18.379533800 +0300
@@ -2706,7 +2706,7 @@
       QUIT;
 
 #if 1
-      execute_command (command->value.Connection->first);
+      exec_result = execute_command (command->value.Connection->first);
 #else
       execute_command_internal (command->value.Connection->first,
                                  asynchronous, pipe_in, pipe_out,
@@ -2714,10 +2714,15 @@
 #endif
 
       QUIT;
-      optimize_fork (command);                 /* XXX */
-      exec_result = execute_command_internal 
(command->value.Connection->second,
+
+      /* With errfail, the ';' is similar to '&&' */
+      /* Execute the second part, only if first part was OK */
+      if ( !errfail_opt || exec_result == EXECUTION_SUCCESS ) {
+          optimize_fork (command);                     /* XXX */
+          exec_result = execute_command_internal 
(command->value.Connection->second,
                                      asynchronous, pipe_in, pipe_out,
                                      fds_to_close);
+      } ;
       executing_list--;
       break;
 
diff -ru orig/bash-master/flags.c new/bash-master/flags.c
--- orig/bash-master/flags.c    2022-01-05 00:03:45.000000000 +0200
+++ new/bash-master/flags.c     2022-07-05 13:42:12.287799400 +0300
@@ -156,6 +156,12 @@
    with a 0 status, the status of the pipeline is 0. */
 int pipefail_opt = 0;
 
+/* Non-zero means that when executing connected commands (';' or new lines)
+   the sequence will be stopped when  any individual commands return a non-zero
+   status. Similar to '&&'. Used for improved error handling */
+
+int errfail_opt = 0;
+
 /* **************************************************************** */
 /*                                                                 */
 /*                     The Flags ALIST.                            */
diff -ru orig/bash-master/flags.h new/bash-master/flags.h
--- orig/bash-master/flags.h    2022-01-05 00:03:45.000000000 +0200
+++ new/bash-master/flags.h     2022-07-05 11:25:14.515741700 +0300
@@ -48,7 +48,7 @@
   echo_command_at_execute, noclobber,
   hashing_enabled, forced_interactive, privileged_mode, jobs_m_flag,
   asynchronous_notification, interactive_comments, no_symbolic_links,
-  function_trace_mode, error_trace_mode, pipefail_opt;
+  function_trace_mode, error_trace_mode, pipefail_opt, errfail_opt;
 
 /* -c, -s invocation options -- not really flags, but they show up in $- */
 extern int want_pending_command, read_from_stdin;

Reply via email to