[RFC PATCH 07/11] cmd: echo: Use getopt() with '+' prefix for option parsing
Simon Glass
sjg at chromium.org
Fri May 15 22:32:58 CEST 2026
The 'echo' command's option parser is a single strcmp against argv[1]
that decides whether to suppress the trailing newline. Convert it to
getopt() so echo follows the same shape as the other commands in this
series and exercises the '+' prefix that POSIX-style 'stop at first
non-option' callers need.
The optstring uses the '+' prefix to preserve bash echo behaviour: -n is
honoured only as the very first argument, so 'echo hello -n' still
prints 'hello -n\n' verbatim.
Two minor differences from the bash builtin remain, both of which
the user can work around with quoting or --:
* -x (an unknown short option) returns CMD_RET_USAGE rather than
printing literally; use 'echo -- -x' to print it.
* -nfoo (joined form) parses -n and then errors on the trailing
characters.
Add three test cases that pin down the new behaviour: trailing -n stays
literal, -- ends option parsing, and -- is consumed even after a
recognised flag. The positional loop uses getopt_pop() as an iterator.
CMD_ECHO selects GETOPT so the parser is linked in on boards that don't
already enable it.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
cmd/Kconfig | 1 +
cmd/echo.c | 23 ++++++++++++++---------
test/cmd/test_echo.c | 10 ++++++++++
3 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig
index c71c6824a19..709696c3c41 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1916,6 +1916,7 @@ config CMD_CAT
config CMD_ECHO
bool "echo"
default y
+ select GETOPT
help
Echo args to console
diff --git a/cmd/echo.c b/cmd/echo.c
index d1346504cfb..63422c75cc6 100644
--- a/cmd/echo.c
+++ b/cmd/echo.c
@@ -5,27 +5,32 @@
*/
#include <command.h>
-#include <linux/string.h>
+#include <getopt.h>
static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
- int i = 1;
+ struct getopt_state gs;
bool space = false;
bool newline = true;
+ char *arg;
+ int opt;
- if (argc > 1) {
- if (!strcmp(argv[1], "-n")) {
+ getopt_init_state(&gs, argc, argv);
+ while ((opt = getopt(&gs, "+n")) > 0) {
+ switch (opt) {
+ case 'n':
newline = false;
- ++i;
+ break;
+ default:
+ return CMD_RET_USAGE;
}
}
- for (; i < argc; ++i) {
- if (space) {
+ while ((arg = getopt_pop(&gs))) {
+ if (space)
putc(' ');
- }
- puts(argv[i]);
+ puts(arg);
space = true;
}
diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c
index 7ed534742f7..5fc139fbe68 100644
--- a/test/cmd/test_echo.c
+++ b/test/cmd/test_echo.c
@@ -35,6 +35,16 @@ static struct test_data echo_data[] = {
/* Test handling of shell variables. */
{"setenv jQx; for jQx in 1 2 3; do echo -n \"${jQx}, \"; done; echo;",
"1, 2, 3, "},
+ /* -n only suppresses the newline when it comes before any
+ * positional argument; a trailing -n is just another argument.
+ */
+ {"echo hello -n", "hello -n"},
+ /* "--" ends option parsing, so a following dash-prefixed token
+ * is printed verbatim instead of being rejected.
+ */
+ {"echo -- -x", "-x"},
+ /* "--" is consumed even when it follows a recognised flag. */
+ {"echo -n -- foo; echo done", "foodone"},
};
static int lib_test_hush_echo(struct unit_test_state *uts)
--
2.43.0
More information about the U-Boot
mailing list