This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 35f3690a4 nshlib: Add stderr redirection support
35f3690a4 is described below

commit 35f3690a4c46ed8e81312d7211652bd5358a57c9
Author: fangpeina <[email protected]>
AuthorDate: Tue Dec 16 23:09:50 2025 +0800

    nshlib: Add stderr redirection support
    
    This commit implements stderr redirection in HSN, support
    Bash-like syntax for redirecting standard error output.
    Support both foreground and background commands.
    
    example:
    - nsh> ls noexists 2> err.log
    - nsh> ls noexists 2> err.log &
    - nsh> ls noexists 2>> err.log
    - nsh> sh < /dev/ttyS0 > /dev/ttyS0 2> /dev/ttyS1 &
    - nsh> sh < /dev/ttyS0 > /dev/ttyS0 2>&1 &
    
    Signed-off-by: fangpeina <[email protected]>
---
 include/nshlib/nshlib.h |  11 +++--
 nshlib/nsh.h            |   1 +
 nshlib/nsh_console.c    |   5 +-
 nshlib/nsh_console.h    |  20 ++++----
 nshlib/nsh_fileapps.c   |  33 ++++++++++++++
 nshlib/nsh_parse.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++---
 nshlib/nsh_script.c     |   2 +-
 7 files changed, 168 insertions(+), 23 deletions(-)

diff --git a/include/nshlib/nshlib.h b/include/nshlib/nshlib.h
index f9565894a..f2153fed0 100644
--- a/include/nshlib/nshlib.h
+++ b/include/nshlib/nshlib.h
@@ -69,21 +69,24 @@
 
 struct nsh_param_s
 {
-  /* Redirect input/output through `fd` OR `path_name`
+  /* Redirect input/output/error through `fd` OR `path_name`
    *
    * Select one:
-   * 1. Using fd_in/fd_out as oldfd for dup2() if greater than -1.
-   * 2. Using file_in/file_out as full path to the file if it is
-   *    not NULL, and oflags_in/oflags_out as flags for open().
+   * 1. Using fd_in/fd_out/fd_err as oldfd for dup2() if greater than -1.
+   * 2. Using file_in/file_out/file_err as full path to the file if it is
+   *    not NULL, and oflags_in/oflags_out/oflags_err as flags for open().
    */
 
   int fd_in;
   int fd_out;
+  int fd_err;
 
   int oflags_in;
   int oflags_out;
+  int oflags_err;
   FAR const char *file_in;
   FAR const char *file_out;
+  FAR const char *file_err;
 };
 
 /****************************************************************************
diff --git a/nshlib/nsh.h b/nshlib/nsh.h
index 74f4032cc..8ea334d07 100644
--- a/nshlib/nsh.h
+++ b/nshlib/nsh.h
@@ -655,6 +655,7 @@ struct nsh_parser_s
 #endif
   bool     np_redir_out; /* true: Output from the last command was re-directed 
*/
   bool     np_redir_in;  /* true: Input from the last command was re-directed 
*/
+  bool     np_redir_err; /* true: Error from the last command was re-directed 
*/
   bool     np_fail;      /* true: The last command failed */
   pid_t    np_lastpid;   /* Pid of the last command executed */
 #ifdef NSH_HAVE_VARS
diff --git a/nshlib/nsh_console.c b/nshlib/nsh_console.c
index 2d8c315ad..cb95e0342 100644
--- a/nshlib/nsh_console.c
+++ b/nshlib/nsh_console.c
@@ -76,7 +76,7 @@ static int nsh_erroroutput(FAR struct nsh_vtbl_s *vtbl,
 #endif
 static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl);
 static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in,
-                                int fd_out, FAR uint8_t *save);
+                                int fd_out, int fd_err, FAR uint8_t *save);
 static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl,
                                 FAR uint8_t *save);
 static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl,
@@ -332,7 +332,7 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl)
  ****************************************************************************/
 
 static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in,
-                                int fd_out, FAR uint8_t *save)
+                                int fd_out, int fd_err, FAR uint8_t *save)
 {
   FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl;
   FAR struct serialsave_s *ssave  = (FAR struct serialsave_s *)save;
@@ -354,6 +354,7 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s 
*vtbl, int fd_in,
 
   OUTFD(pstate) = fd_out;
   INFD(pstate) = fd_in;
+  ERRFD(pstate) = fd_err;
 }
 
 /****************************************************************************
diff --git a/nshlib/nsh_console.h b/nshlib/nsh_console.h
index 0b94a2b6b..268d03379 100644
--- a/nshlib/nsh_console.h
+++ b/nshlib/nsh_console.h
@@ -43,15 +43,15 @@
 
 /* Method access macros */
 
-#define nsh_clone(v)            (v)->clone(v)
-#define nsh_release(v)          (v)->release(v)
-#define nsh_write(v,b,n)        (v)->write(v,b,n)
-#define nsh_read(v,b,n)         (v)->read(v,b,n)
-#define nsh_ioctl(v,c,a)        (v)->ioctl(v,c,a)
-#define nsh_linebuffer(v)       (v)->linebuffer(v)
-#define nsh_redirect(v,fi,fo,s) (v)->redirect(v,fi,fo,s)
-#define nsh_undirect(v,s)       (v)->undirect(v,s)
-#define nsh_exit(v,s)           (v)->exit(v,s)
+#define nsh_clone(v)               (v)->clone(v)
+#define nsh_release(v)             (v)->release(v)
+#define nsh_write(v,b,n)           (v)->write(v,b,n)
+#define nsh_read(v,b,n)            (v)->read(v,b,n)
+#define nsh_ioctl(v,c,a)           (v)->ioctl(v,c,a)
+#define nsh_linebuffer(v)          (v)->linebuffer(v)
+#define nsh_redirect(v,fi,fo,fe,s) (v)->redirect(v,fi,fo,fe,s)
+#define nsh_undirect(v,s)          (v)->undirect(v,s)
+#define nsh_exit(v,s)              (v)->exit(v,s)
 
 #ifdef CONFIG_CPP_HAVE_VARARGS
 #  define nsh_error(v, ...)     (v)->error(v, ##__VA_ARGS__)
@@ -127,7 +127,7 @@ struct nsh_vtbl_s
       printf_like(2, 3);
   FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl);
   void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd_in, int fd_out,
-                   FAR uint8_t *save);
+                   int fd_err, FAR uint8_t *save);
   void (*undirect)(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save);
   void (*exit)(FAR struct nsh_vtbl_s *vtbl, int status) noreturn_function;
 
diff --git a/nshlib/nsh_fileapps.c b/nshlib/nsh_fileapps.c
index 54efd6de2..66f41e36f 100644
--- a/nshlib/nsh_fileapps.c
+++ b/nshlib/nsh_fileapps.c
@@ -177,6 +177,39 @@ int nsh_fileapp(FAR struct nsh_vtbl_s *vtbl, FAR const 
char *cmd,
             }
         }
 #endif
+
+      /* Handle redirection of error output */
+
+      if (param->file_err)
+        {
+          /* 2> file: Redirect stderr to a file */
+
+          ret = posix_spawn_file_actions_addopen(&file_actions, 2,
+                                                  param->file_err,
+                                                  param->oflags_err,
+                                                  0644);
+          if (ret != 0)
+            {
+              nsh_error(vtbl, g_fmtcmdfailed, cmd,
+                        "posix_spawn_file_actions_addopen",
+                        NSH_ERRNO);
+              goto errout_with_attrs;
+            }
+        }
+      else if (param->fd_err != -1)
+        {
+          /* 2>&1: Redirect stderr to stdout */
+
+          ret = posix_spawn_file_actions_adddup2(&file_actions,
+                                                 param->fd_err, 2);
+          if (ret != 0)
+            {
+              nsh_error(vtbl, g_fmtcmdfailed, cmd,
+                        "posix_spawn_file_actions_adddup2",
+                        NSH_ERRNO);
+              goto errout_with_attrs;
+            }
+        }
     }
 
 #ifdef CONFIG_BUILTIN
diff --git a/nshlib/nsh_parse.c b/nshlib/nsh_parse.c
index 9b1bde875..9a67cc845 100644
--- a/nshlib/nsh_parse.c
+++ b/nshlib/nsh_parse.c
@@ -264,6 +264,12 @@ static const char   g_redirect_out2[]   = ">>";
 static const size_t g_redirect_out2_len = sizeof(g_redirect_out2) - 1;
 static const char   g_redirect_in1[]    = "<";
 static const size_t g_redirect_in1_len  = sizeof(g_redirect_in1) - 1;
+static const char   g_redirect_err1[]   = "2>";
+static const size_t g_redirect_err1_len = sizeof(g_redirect_err1) - 1;
+static const char   g_redirect_err2[]   = "2>>";
+static const size_t g_redirect_err2_len = sizeof(g_redirect_err2) - 1;
+static const char   g_redirect_err3[]   = "2>&1";
+static const size_t g_redirect_err3_len = sizeof(g_redirect_err3) - 1;
 #ifdef CONFIG_NSH_PIPELINE
 static const char   g_pipeline1[]       = "|";
 static const size_t g_pipeline1_len     = sizeof(g_pipeline1) - 1;
@@ -503,6 +509,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
                        FAR const struct nsh_param_s *param)
 {
   int fd_out = STDOUT_FILENO;
+  int fd_err = STDERR_FILENO;
   int fd_in = STDIN_FILENO;
   int ret;
 
@@ -703,11 +710,36 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
             }
         }
 
-      /* Handle redirection of stdin/stdout file descriptor */
+      /* Redirected error output? */
 
-      if (vtbl->np.np_redir_out || vtbl->np.np_redir_in)
+      if (vtbl->np.np_redir_err)
         {
-          nsh_redirect(vtbl, fd_in, fd_out, save);
+          if (param->file_err)
+            {
+              /* 2> file: Open the redirection file for stderr */
+
+              fd_err = open(param->file_err, param->oflags_err, 0666);
+              if (fd_err < 0)
+                {
+                  nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open",
+                            NSH_ERRNO);
+                  return nsh_saveresult(vtbl, true);
+                }
+            }
+          else
+            {
+              /* 2>&1: redirect stderr to current stdout fd */
+
+              fd_err = fd_out;
+            }
+        }
+
+      /* Handle redirection of stdin/stdout/stderr file descriptor */
+
+      if (vtbl->np.np_redir_out || vtbl->np.np_redir_in ||
+          vtbl->np.np_redir_err)
+        {
+          nsh_redirect(vtbl, fd_in, fd_out, fd_err, save);
         }
 
       /* Then execute the command in "foreground" -- i.e., while the user
@@ -723,7 +755,8 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
        * file descriptor.
        */
 
-      if (vtbl->np.np_redir_out || vtbl->np.np_redir_in)
+      if (vtbl->np.np_redir_out || vtbl->np.np_redir_in ||
+          vtbl->np.np_redir_err)
         {
           nsh_undirect(vtbl, save);
           fd_out = -1;
@@ -885,10 +918,13 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, 
FAR char *cmdline,
     {
       .fd_in      = -1,
       .fd_out     = -1,
+      .fd_err     = -1,
       .oflags_in  = 0,
       .oflags_out = O_WRONLY | O_CREAT | O_TRUNC,
+      .oflags_err = O_WRONLY | O_CREAT | O_TRUNC,
       .file_in    = NULL,
-      .file_out   = NULL
+      .file_out   = NULL,
+      .file_err   = NULL
     };
 
   FAR char *tmpfile;
@@ -2447,10 +2483,13 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s 
*vtbl, FAR char *cmdline)
     {
       .fd_in      = -1,
       .fd_out     = -1,
+      .fd_err     = -1,
       .oflags_in  = 0,
       .oflags_out = 0,
+      .oflags_err = 0,
       .file_in    = NULL,
-      .file_out   = NULL
+      .file_out   = NULL,
+      .file_err   = NULL
     };
 
 #ifdef CONFIG_NSH_PIPELINE
@@ -2469,6 +2508,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, 
FAR char *cmdline)
   int       ret;
   bool      redirect_out_save = false;
   bool      redirect_in_save = false;
+  bool      redirect_err_save = false;
 #ifdef CONFIG_NSH_PIPELINE
   bool      bg_save = false;
 #endif
@@ -2492,6 +2532,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, 
FAR char *cmdline)
 
   vtbl->np.np_redir_out = false;
   vtbl->np.np_redir_in = false;
+  vtbl->np.np_redir_err = false;
 
   /* Parse out the command at the beginning of the line */
 
@@ -2692,6 +2733,60 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s 
*vtbl, FAR char *cmdline)
           param.oflags_in       = O_RDONLY;
           param.file_in         = nsh_getfullpath(vtbl, arg);
         }
+      else if (!strncmp(argv[argc], g_redirect_err3, g_redirect_err3_len))
+        {
+          redirect_err_save     = vtbl->np.np_redir_err;
+          vtbl->np.np_redir_err = true;
+          param.fd_err          = STDOUT_FILENO;
+        }
+      else if (!strncmp(argv[argc], g_redirect_err2, g_redirect_err2_len))
+        {
+          FAR char *arg;
+          if (argv[argc][g_redirect_err2_len])
+            {
+              arg = &argv[argc][g_redirect_err2_len];
+            }
+          else
+            {
+              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
+            }
+
+          if (!arg)
+            {
+              nsh_error(vtbl, g_fmtarginvalid, cmd);
+              ret = ERROR;
+              goto dynlist_free;
+            }
+
+          redirect_err_save     = vtbl->np.np_redir_err;
+          vtbl->np.np_redir_err = true;
+          param.oflags_err      = O_WRONLY | O_CREAT | O_APPEND;
+          param.file_err        = nsh_getfullpath(vtbl, arg);
+        }
+      else if (!strncmp(argv[argc], g_redirect_err1, g_redirect_err1_len))
+        {
+          FAR char *arg;
+          if (argv[argc][g_redirect_err1_len])
+            {
+              arg = &argv[argc][g_redirect_err1_len];
+            }
+          else
+            {
+              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
+            }
+
+          if (!arg)
+            {
+              nsh_error(vtbl, g_fmtarginvalid, cmd);
+              ret = ERROR;
+              goto dynlist_free;
+            }
+
+          redirect_err_save     = vtbl->np.np_redir_err;
+          vtbl->np.np_redir_err = true;
+          param.oflags_err      = O_WRONLY | O_CREAT | O_TRUNC;
+          param.file_err        = nsh_getfullpath(vtbl, arg);
+        }
 #ifdef CONFIG_NSH_PIPELINE
       else if (!strncmp(argv[argc], g_pipeline1, g_pipeline1_len))
         {
@@ -2848,6 +2943,18 @@ dynlist_free:
     }
 #endif
 
+  /* Free the redirected error file path and restore state */
+
+  if (param.file_err)
+    {
+      nsh_freefullpath((char *)param.file_err);
+    }
+
+  if (vtbl->np.np_redir_err)
+    {
+      vtbl->np.np_redir_err = redirect_err_save;
+    }
+
   NSH_ALIASLIST_FREE(vtbl, &alist);
   NSH_MEMLIST_FREE(&memlist);
 #ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
diff --git a/nshlib/nsh_script.c b/nshlib/nsh_script.c
index a0db4d6d0..21bef19e4 100644
--- a/nshlib/nsh_script.c
+++ b/nshlib/nsh_script.c
@@ -64,7 +64,7 @@ static int nsh_script_redirect(FAR struct nsh_vtbl_s *vtbl,
       fd = open(CONFIG_NSH_SCRIPT_REDIRECT_PATH, 0666);
       if (fd > 0)
         {
-          nsh_redirect(vtbl, 0, fd, save);
+          nsh_redirect(vtbl, 0, fd, fd, save);
         }
     }
 

Reply via email to