1

GNU bash, version 3.2.48 has this bug; already version 3.2.57 does not.

Make a file with 8000 identical lines (say each line says 1). Run split -a3 -p "1" on it (-p is a BSD split option which makes it split on the given pattern. For a file with just one 1 per line, you can do the same thing with a standard split by running split -a3 -b 1). And execute

find . -name xaaa -exec echo {} +

And after the expected output, you get find: fts_read: Invalid argument output to cerr. The same error occurs when xaaa is replaced by any set of files, and echo by any other command I've tried. The length of the filename doesn't matter. The directory of the files also doesn't matter.

After some creating files elsewhere, the error is gone. However, when xaaa is replaced by xaa* (or any other wildcard that includes multiple files, at least one of which is near the beginning of the directory listing), then the error occurs again. At that point, no single file causes the error to appear.

Replacing + with ; does avoid the error, but is not acceptable for my script. This problem has been occurring intermittently in other situations in my script, but by reducing it I was able to come up with a simple way of replicating it.

I want the script to stop if an error occurs, but this just makes it stop very often. Any idea how to get around this? (e.g. retrieve an error code and ignore just this specific error).

Mac OS version 10.8.5. Darwin Kernel Version 12.5.0.

terdon
  • 242,166
Alex
  • 1,175
  • Bash is not involved anywhere in the command you have shown us. find ... -exec echo ... is going to execute /bin/echo (or whichever echo command appears first in $PATH), and doesn't involve a shell at all. – muru Oct 25 '23 at 03:45
  • As I said above, it doesn't depend on the command used. Replacing echo with cat or ls, for example, still throws the same error. And since it appears when files near the beginning of the directory listing are involved, I suspect find is the culprit. – Alex Oct 25 '23 at 03:47
  • I see. I put it there in case anyone wanted to replicate it, and then complain that no error occurred on their end. Quite plausibly, something other than the bash version is responsible, but I just don`t know what to check. – Alex Oct 25 '23 at 03:50
  • For that, the OS version would be useful (presumably you're on macOS?), or maybe even just uname -r or uname -v. Have you checked what the exit status of find is when you get this error using $?? – muru Oct 25 '23 at 03:55
  • I updated the question to include the OS version and the Kernel version. find's exit status is 1, of course, as on any other error. – Alex Oct 25 '23 at 03:59
  • fts_read (3) - traverse a file hierarchy: a C library function called by find. No external process here, no exit status. Gdb with a break on the function, and compare its args with the man page ? – Paul_Pedant Oct 25 '23 at 10:47
  • I read that before asking. But I don't know if I can gdb when I don't have the source code for find... Unless it's available somewhere. – Alex Oct 25 '23 at 13:08

1 Answers1

1

There's very little chance bash would be involved in there.

bash's role in that is to parse that find . -name xaaa -exec echo {} + code and turn that into the execution of /path/to/find with find, ., -name, xaaa, -exec, echo, {}, +. Any bash version would do the same.

Here, as the find: prefix in the error message, the error is by find, not bash, and specifically is about an error returned by the fts_read() function.

macos' find is that of FreeBSD possibly with modifications by Apple.

Thankfully, FreeBSD (if not macos) is FLOSS, and it's relatively easy to spot where that error is output in the code there.

/*
 * find_execute --
 *  take a search plan and an array of search paths and executes the plan
 *  over all FTSENT's returned for the given search paths.
 */
int
find_execute(PLAN *plan, char *paths[])
[...]
    e = errno;
    finish_execplus();
    if (e && (!ignore_readdir_race || e != ENOENT))
        errc(1, e, "fts_read");
    return (exitstatus);
}

At the very end of that find_execute function.

Here we get:

 find: fts_read: Invalid argument

Invalid argument being the error message that corresponds to the EINVAL errno.

Here errno would have been set by fts_read as errno is set to 0 in the condition statement of the while loop just before calling fts_read and since we're out of the loop, that means fts_read just returned with NULL.

If you look at the man page of fts_read, you'll find:

The functions fts_read() and fts_children() may fail and set errno for any of the errors specified for the library functions chdir(2), malloc(3), opendir(3), readdir(3) and stat(2).

If you follow those links to the other man pages, none of them is meant to return EINVAL.

So it very much looks like a bug either in Apple's find or the fts library there. I'd suggest you raise that to Apple support as without access to the code, there's likely little more any of us could do here.