A digression on echo. - Other *nix-like OSes & POSIX related

Users browsing this thread: 1 Guest(s)
Long time nixers
After commenting on this thread I remembered a text on echo that Brian Kernighan and Rob Pike wrote in their book The UNIX Programming Environment called A digression on echo.

[Image: j5kXsXV.png]

The single option -n is not like other options in other utilities.
For example, while most utilities use getopt(3) to get the options, echo(1) should not use it, since a gotten option could be an argument to be echoed. See how OpenBSD implement echo, for example:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

main(int argc, char *argv[])
    int nflag;

    if (pledge("stdio", NULL) == -1)
        err(1, "pledge");

    /* This utility may NOT do getopt(3) option parsing. */
    if (*++argv && !strcmp(*argv, "-n")) {
        nflag = 1;
        nflag = 0;

    while (*argv) {
        (void)fputs(*argv, stdout);
        if (*++argv)
            putchar(' ');
    if (!nflag)

    return 0;

Other than -n, modern echo implementations also add a new complexity: the C-like backslash sequences like '\n' and '\t'. Some echo implementations (such as OpenBSD's) just ignore them, but some (such as Bash built-in) don't. Here is another digression on echo about this very topic, by Doug Mcllroy, best known for proposing the UNIX pipelines and creating several UNIX tools:

[Image: gFPe9sR.png]

There is, however, another utility that does not print newlines when not asked for and that does interpret C-like backslash sequences: printf(1)

PS: I don't know the right forum to post it. I posted it here in the Philosophy forum since I think that the question on bloatedness and feature creep are related to UNIX Philosophy.
Long time nixers
This seems to be one of the better examples on how Unix defaults to non-predictive behavior sometimes.
Grey Hair Nixers
Good job digging up the history behind it. it was a really interesting read !

Reading the source code for echo under different implementations has been an exercise many people have taken to compare code complexity (obviously, GNU "wins" here).

I personally only use "echo" to print out debug statements, or quickly output a variable name. For example, all my scripts feature this snippet:

usage() {
    echo "usage: $(basename $0) [-flags] args..." >&2

Whenever I need to print out formatted stuff, printf(1) is my goto as it is much more convenient.
Long time nixers
(20-04-2020, 03:40 PM)z3bra Wrote: Reading the source code for echo under different implementations has been an exercise many people have taken to compare code complexity (obviously, GNU "wins" here).

This comparison remembered me of this meme, based on longcat.

[Image: 7r5Q1qA.png]
Long time nixers
I wonder how much of the GNU code is a try to circumvent the limitations imposed by its libc compared to Plan 9’s.
Grey Hair Nixers
Hahaha awesome meme !

(20-04-2020, 04:33 PM)jkl Wrote: I wonder how much of the GNU code is a try to circumvent the limitations imposed by its libc compared to Plan 9’s.

I think they're more trying to circumvent their own coding guidelines than the libc… Which is even more terrifying if you ask me. I mean. what the hell is this? or that? I'm speechless…
Long time nixers
"GNU's Not Unix. And here's how we mean that."
Reading the GNU coding standards was what convinced me to move back to OpenBSD, after having had to move back to Linux for certain software support which is no longer an issue. In particular, this paragraph:

Quote: For instance, Standard C says that nearly all extensions to C are
prohibited. How silly! GCC implements many extensions, some of which
were later adopted as part of the standard. If you want these
constructs to give an error message as "required" by the standard, you
must specify '--pedantic', which was implemented only so that we can say
"GCC is a 100% implementation of the standard", not because there is any
reason to actually use it.

It reads to me both as quite immature and as something of a "Microsoftian" attitude. Every time I see GNU code I find it offputting, and I wouldn't even really consider myself a UNIX purist.
Long time nixers
Another GNUism that turned out into a meme:
GNU true(1), an utility that is supposed to return zero (true), can return non-zero (false).
That's because it does way more than return 0.

Compare OpenBSD implementation of true(1) with GNU's.

/*    $OpenBSD: true.c,v 1.1 2015/11/11 19:05:28 deraadt Exp $    */

/* Public domain - Theo de Raadt */

main(int argc, char *argv[])
    return (0);

/* Exit with a status code indicating success.
   Copyright (C) 1999-2020 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"

/* Act like "true" by default; false.c overrides this.  */

# define PROGRAM_NAME "true"
# define PROGRAM_NAME "false"

#define AUTHORS proper_name ("Jim Meyering")

usage (int status)
  printf (_("\
Usage: %s [ignored command line arguments]\n\
  or:  %s OPTION\n\
          program_name, program_name);
  printf ("%s\n\n",
            ? N_("Exit with a status code indicating success.")
            : N_("Exit with a status code indicating failure.")));
  fputs (HELP_OPTION_DESCRIPTION, stdout);
  emit_ancillary_info (PROGRAM_NAME);
  exit (status);

main (int argc, char **argv)
  /* Recognize --help or --version only if it's the only command-line
     argument.  */
  if (argc == 2)
      initialize_main (&argc, &argv);
      set_program_name (argv[0]);
      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      /* Note true(1) will return EXIT_FAILURE in the
         edge case where writes fail with GNU specific options.  */
      atexit (close_stdout);

      if (STREQ (argv[1], "--help"))
        usage (EXIT_STATUS);

      if (STREQ (argv[1], "--version"))
        version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
                     (char *) NULL);

  return EXIT_STATUS;