1
ls -R /home/username/some/path

is the same as

ls - R /home///username/////////////some////path

Is there explanation for such interpretation of '/' character in bash?

I can't see implementation of ls program, but I can see my implementation of it:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>


/* This program recursively prints files from given path. Should 
be equivalent to ls -R /given/path call from shell, except we 
don't concatenate '@' character at the end of symbolic link name, 
'/' if directory entry is directory, we don't sort output etc.. */

static void error_fatal (char *message);

static void traverse (char *filename);

int
main (int argc, char **argv)
{

assert (argc == 2);

traverse (argv[1]);

exit (EXIT_SUCCESS);
}

static void
traverse (char *filename)
{
struct stat stats;
DIR *dir;
struct dirent *entry;


  if (lstat (filename, &stats) < 0)
    error_fatal ("lstat");

  if (S_ISDIR (stats.st_mode))
{
    printf ("\n%s:\n", filename);

    if ((dir = opendir (filename)) == NULL)
      error_fatal ("opendir");

    if (chdir (filename) < 0)
      error_fatal ("chdir");

// In the first pass, we print all directory entries.

  while ((entry = readdir (dir)) != NULL)
    {
    if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, 
     ".."))
      continue;
    printf ("%s\t", entry->d_name);
}
  putchar ('\n');

  rewinddir (dir);

  // In the second pass, we examine if entry is directory, and if so, recursively call traverse() function.

  while ((entry = readdir (dir)) != NULL)
{
  if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
    continue;

  if (lstat (entry->d_name, &stats) < 0)
    error_fatal ("lstat");

  if (S_ISDIR (stats.st_mode))
    {

      char *a;
      int size =
    sizeof (char) * (2 + strlen (filename) +
             strlen (entry->d_name));
      a = (char *) malloc (size);
      if (a == NULL)
    error_fatal ("malloc");
      if (getcwd (a, size) == NULL)
    error_fatal ("getcwd");
      strcat (a, "/");
      strcat (a, entry->d_name);
      traverse (a);
      free (a);
    }

}


  if (chdir ("..") < 0)
error_fatal ("chdir");

  closedir (dir);

}
}

static void
error_fatal (char *message)
{
  perror (message);
  exit (EXIT_FAILURE);
}

uname -a gives me:

Linux mk-Inspiron-5570 4.15.0-43-generic #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

bash --version gives me:

GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pomsky
  • 68,507
mk1024
  • 669

2 Answers2

6

The reason is simple: because it's a standard which isn't bash specific, but is specified by POSIX standard, see Definitions chapter, section 3.266 Pathname

Multiple successive slashes are considered to be the same as one slash.

These specifications are meant for portability and specify how Unix-like operating system and utilities should behave. bash has to support it if it wants to be used as /bin/sh shell, which on Ubuntu it actually used to be symlinked to /bin/sh. See also What is the point of sh being linked to dash?

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
2

From the The Open Group Base Specifications Issue 7, 2018 edition -> Definitions -> 3.271 Pathname:

A string that is used to identify a file. In the context of POSIX.1-2017, a pathname may be limited to {PATH_MAX} bytes, including the terminating null byte. It has optional beginning <slash> characters, followed by zero or more filenames separated by <slash> characters. A pathname can optionally contain one or more trailing <slash> characters. Multiple successive <slash> characters are considered to be the same as one <slash>, except for the case of exactly two leading <slash> characters.

Note: If a pathname consists of only bytes corresponding to characters from the portable filename character set (see Portable Filename Character Set), <slash> characters, and a single terminating <NUL> character, the pathname will be usable as a character string in all supported locales; otherwise, the pathname might only be a string (rather than a character string). Additionally, since the single-byte encoding of the <slash> character is required to be the same across all locales and to not occur within a multi-byte character, references to a <slash> character within a pathname are well-defined even when the pathname is not a character string. However, this property does not necessarily hold for the remaining characters within the portable filename character set.

Pathname Resolution is defined in detail in Pathname Resolution.

slava
  • 3,887