0

I was reading the man of ls and in the end it talks about the Exit status of ls. It says:

   Exit status:
   0      if OK,
   1      if minor problems (e.g., cannot access subdirectory),
   2      if serious trouble (e.g., cannot access command-line argument).

But the thing is that i don't understand what they mean by:

cannot access command-line argument

I have never encountered a situation where i could not acces the arguments passed to my program and i was searching on the web about this particular situation and i couldn't get many information other then this site which wasn't really clear to me and i was unable to reproduce the error. I'm not sure if i'm miss understanding the MAN page

  • 1
    Note that this is specific to GNU ls. A standard-compliant ls just needs to exit with a positive non-zero exit status when encountering an error. – Kusalananda Oct 24 '18 at 7:14
3

For GNU ls, use the source Luke: http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/ls.c;h=bf0c5941d7de699fc5a85d44461ef29192216d9d;hb=HEAD

You will find many cases where the return code is 2, and some are easy to trigger, as shown below.

First you can read in it:

 802 /* Exit statuses.  */
 803 enum
 804   {
 805     /* "ls" had a minor problem.  E.g., while processing a directory,
 806        ls obtained the name of an entry via readdir, yet was later
 807        unable to stat that name.  This happens when listing a directory
 808        in which entries are actively being removed or renamed.  */
 809     LS_MINOR_PROBLEM = 1,
 810 
 811     /* "ls" had more serious trouble (e.g., memory exhausted, invalid
 812        option or failure to stat a command line argument.  */
 813     LS_FAILURE = 2
 814   };

So you can see already that value 2 covers more cases than what is written in the documentation.

Then if you search further for LS_FAILURE in the code you find out about different cases:

Case 1

1896         case 'w':
1897           if (! set_line_length (optarg))
1898             die (LS_FAILURE, 0, "%s: %s", _("invalid line width"),
1899                  quote (optarg));
1900           break;

set_line_length will react depending on how xstrtoumax returns for the given width. If you look closer at the source code of it you can arrive at some edge cases:

$ ls -w -1 >& /dev/null
$ echo $?
2
$ ls -w 1 >& /dev/null
$ echo $?
0

Case 2

1964         case 'T':
1965           tabsize = xnumtoumax (optarg, 0, 0, SIZE_MAX, "",
1966                                 _("invalid tab size"), LS_FAILURE);
1967           break;

Similar to previous case:

$ ls -T 1 >& /dev/null
$ echo $?
0
$ ls -T -1 >& /dev/null
$ echo $?
2

Case 3

2106         default:
2107           usage (LS_FAILURE);

So that is the default error code if you provide invalid parameters. See this example:

$ ls --unknown-option >& /dev/null
$ echo $?
2

Case 4

2198               if (strchr (p1 + 1, '\n'))
2199                 die (LS_FAILURE, 0, _("invalid time style format %s"),
2200                      quote (p0));

This happens when you are providing invalid time format, with two \n:

$ ls -l --time-style=+%T >& /dev/null ; echo $?
0
$ ls -l --time-style=+%T$'\n' >& /dev/null ; echo $?
0
$ ls -l --time-style=+%T$'\n'%T >& /dev/null ; echo $?
0
$ ls -l --time-style=+%T$'\n'%T$'\n' >& /dev/null ; echo $?
2

Case 5

2218               /* The following is a manual expansion of argmatch_valid,
2219                  but with the added "+ ..." description and the [posix-]
2220                  prefixes prepended.  Note that this simplification works
2221                  only because all four existing time_style_types values
2222                  are distinct.  */
2223               fputs (_("Valid arguments are:\n"), stderr);
2224               char const *const *p = time_style_args;
2225               while (*p)
2226                 fprintf (stderr, "  - [posix-]%s\n", *p++);
2227               fputs (_("  - +FORMAT (e.g., +%H:%M) for a 'date'-style"
2228                        " format\n"), stderr);
2229               usage (LS_FAILURE);

Triggered when using invalid time format name:

$ LANG=C ls -l --time-style=whatever 
ls: invalid argument 'whatever' for 'time style'
Valid arguments are:
  - [posix-]full-iso
  - [posix-]long-iso
  - [posix-]iso
  - [posix-]locale
  - +FORMAT (e.g., +%H:%M) for a 'date'-style format
Try 'ls --help' for more information.

$ echo $?
2

Case 6

2669 static void
2670 set_exit_status (bool serious)
2671 {
2672   if (serious)
2673     exit_status = LS_FAILURE;
2674   else if (exit_status == EXIT_SUCCESS)
2675     exit_status = LS_MINOR_PROBLEM;
2676 }

This (serious = true) can happen in multiple cases, for example if there is a loop somewhere:

2747       /* If we've already visited this dev/inode pair, warn that
2748          we've found a loop, and do not process this directory.  */
2749       if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
2750         {
2751           error (0, 0, _("%s: not listing already-listed directory"),
2752                  quotef (name));
2753           closedir (dirp);
2754           set_exit_status (true);
2755           return;
2756         }

It can also happen for many other cases, based on arguments. file_failure first argument is the boolean passed to set_exit_status

Subcase A

2710 /* Read directory NAME, and list the files in it.
2711    If REALNAME is nonzero, print its name instead of NAME;
2712    this is used for symbolic links to directories.
2713    COMMAND_LINE_ARG means this directory was mentioned on the command line.  */

...

2725   if (!dirp)
2726     {
2727       file_failure (command_line_arg, _("cannot open directory %s"), name);
2728       return;
2729     }

So for example:

$ ls /thatDOESnotEXIST >& /dev/null
$ echo $?
2

Subcase B

2736       /* If dirfd failed, endure the overhead of using stat.  */
2737       if ((0 <= fd
2738            ? fstat (fd, &dir_stat)
2739            : stat (name, &dir_stat)) < 0)
2740         {
2741           file_failure (command_line_arg,
2742                         _("cannot determine device and inode of %s"), name);

That is some kind of directory not available to access (like a remote one).

Subcase C

2771       if (print_hyperlink)
2772         {
2773           absolute_name = canonicalize_filename_mode (name, CAN_MISSING);
2774           if (! absolute_name)
2775             file_failure (command_line_arg,
2776                           _("error canonicalizing %s"), name);

or

3189       if (print_hyperlink)
3190         {
3191           f->absolute_name = canonicalize_filename_mode (full_name,
3192                                                          CAN_MISSING);
3193           if (! f->absolute_name)
3194             file_failure (command_line_arg,
3195                           _("error canonicalizing %s"), full_name);

or

3450 static void
3451 get_link_name (char const *filename, struct fileinfo *f, bool command_line_arg)
3452 {
3453   f->linkname = areadlink_with_size (filename, f->stat.st_size);
3454   if (f->linkname == NULL)
3455     file_failure (command_line_arg, _("cannot read symbolic link %s"),
3456                   filename);
3457 }

These are some kind of broken hard/soft links.

Subcase D

2836       else if (errno != 0)
2837         {
2838           file_failure (command_line_arg, _("reading directory %s"), name);

or

2851   if (closedir (dirp) != 0)
2852     {
2853       file_failure (command_line_arg, _("closing directory %s"), name);

Another case when it is not possible to read directory content (if provided on command line)

Subcase E

3235       if (err != 0)
3236         {
3237           /* Failure to stat a command line argument leads to
3238              an exit status of 2.  For other files, stat failure
3239              provokes an exit status of 1.  */
3240           file_failure (command_line_arg,
3241                         _("cannot access %s"), full_name);

That happens when trying to match files, such as:

$ ls '*DOESnotEXIST*' >& /dev/null
$ echo $?
2
  • Your answer is about gls, not ls... the standard says exit > 0 in case of errors, not more. – schily Oct 24 '18 at 10:42
  • @schily the question is not about the standard but about what the OP sees in his ls manual which clearly shows exit codes 1 and 2. – Patrick Mevzek Oct 24 '18 at 13:16
  • But since he did not mention which ls implementation he is referring to, you should not make assumptions based on gls. – schily Oct 24 '18 at 13:31
  • @schily look at the man output from the question. Also the command is still called ls even if coming from GNU. – Patrick Mevzek Oct 24 '18 at 14:10
1

Try:

$ ls $(date); echo "$?"
ls: cannot access 'Wed': No such file or directory
ls: cannot access 'Oct': No such file or directory
ls: cannot access '24': No such file or directory
ls: cannot access '02:42:02': No such file or directory
ls: cannot access 'UTC': No such file or directory
ls: cannot access '2018': No such file or directory
exit status 2
  • This is not granted, the standard just requires exitcode > 0 in that case. – schily Oct 24 '18 at 10:43
  • @schily 2 is indeed > 0 so that implementation follows the standard. That still does not explain why 2 instead of any other value and when 2 instead of any other value – Patrick Mevzek Oct 24 '18 at 13:24
  • In the classical UNIX ls, there is no clean rule set for when the exit code is 1 and when it is 2. Maybe this is the reason, why POSIX does not mention it at all. – schily Oct 24 '18 at 13:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.