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);
}
}