[PATCH v4 14/14] cmd: echo: Use getopt() for option parsing

Simon Glass sjg at chromium.org
Wed Jul 1 13:42:34 CEST 2026


The open-coded check for -n only matches it as the first argument, so
'echo foo -n' treats -n as a literal argument while still relying on a
hand-rolled scan that exists in every option-parsing command. Convert
do_echo() to the getopt-state command signature and parse -n with
getopt().

Use a '+' prefix on the optstring so getopt() works in POSIX mode and
stops at the first non-option, matching the documented behaviour that
-n is only an option when it precedes the arguments. The positional
arguments are then printed from gs->argv[gs->index] onwards, so no
manual argc/argv juggling is needed. CMD_ECHO selects GETOPT.

This drops the cmd/echo.o text size from 334 to 320 bytes on sandbox.

Add a test case covering -n after an argument, which is now printed
literally with the trailing newline preserved.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

(no changes since v1)

 cmd/Kconfig          |  1 +
 cmd/echo.c           | 26 ++++++++++++++------------
 test/cmd/test_echo.c |  7 +++++++
 3 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index e100a08d271..8c73b136fa8 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1927,6 +1927,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..ace6b71233b 100644
--- a/cmd/echo.c
+++ b/cmd/echo.c
@@ -5,27 +5,29 @@
  */
 
 #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[])
+static int do_echo(struct getopt_state *gs)
 {
-	int i = 1;
 	bool space = false;
 	bool newline = true;
+	const char *arg;
+	int opt;
 
-	if (argc > 1) {
-		if (!strcmp(argv[1], "-n")) {
+	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;
 	}
 
@@ -35,7 +37,7 @@ static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc,
 	return 0;
 }
 
-U_BOOT_CMD(
+U_BOOT_CMD_GETOPT(
 	echo, CONFIG_SYS_MAXARGS, 1, do_echo,
 	"echo args to console",
 	"[-n] [args..]\n"
diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c
index 7ed534742f7..64589e3656f 100644
--- a/test/cmd/test_echo.c
+++ b/test/cmd/test_echo.c
@@ -22,6 +22,13 @@ static struct test_data echo_data[] = {
 	/* Test new line handling */
 	{"echo -n 1 2 3; echo a b c",
 	 "1 2 3a b c"},
+	/*
+	 * Option parsing stops at the first non-option, so a -n which
+	 * follows an argument is printed literally and the trailing
+	 * newline is still emitted.
+	 */
+	{"echo 1 -n 2",
+	 "1 -n 2"},
 	/*
 	 * Test handling of environment variables.
 	 *
-- 
2.43.0



More information about the U-Boot mailing list