mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
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 <fangpeina@xiaomi.com>
This commit is contained in:
parent
80c2b43197
commit
35f3690a4c
7 changed files with 168 additions and 23 deletions
|
|
@ -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;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue