but `foo` doesn't exist. What should the error message be?
cdexec: error changing directory: foo: No such file or directory
cdexec: error changing directory: foo: ENOENT: No such file or directory
cdexec: error changing directory: foo: ENOENT
More abstractly, which way is best for reporting errors from the system?
...: <errno name>: <perror output>
Personally, I prefer the last one, because:
Including the errno name is very important for one of the biggest users of command-line tools: other programs such as scripts. ENOENT is the standard, international name for the error. The error is ENOENT. The output from perror is a localized human-friendly hint.
Ditto for human users who have grown familiar with the underlying system enough - I've seen developers complain about having to try to figure out what error code a `perror` string maps back to. I have a good enough memory that this doesn't trouble me most of the time, but I still waste a memorized lookup table on it.
The human-friendly error string is misleading in the edge-cases. See, when I see "no such file or directory", I know that there might not be any file or directory involved at all. Did you? For example, some APIs in the Linux network stack report ENOENT for things that aren't in the file system - because of course to a kernel developer living in a world which has missed the value of exposing all things on the "file" system, it makes sense that ENOENT means "no such entity" rather than specifically "no such file or directory". So you run an `ip route ...` command and get your time and energy wasted trying to figure out what file could possibly be missing.
To be clear, this is orthogonal to "should my source code just call `perror` or should I consider adding something like `errnoname`?" Because your libc could easily be made to extend or change the perror output to include errno names, based on either a compile-time flag or for example by setting some environment variable. You could also compile with `perror` renamed or relinked to something else. So we could in principle achieve any of these alternatives while still just calling `perror` in your source.
This is also orthogonal to "but for many users (at least in the vast majority of cases), the human-readable error message is helpful". Just because something is good to have, doesn't mean every single program should contain the code to do it. Turning an error into human-friendly guidance sure looks to me like a composable piece of functionality that can be implemented separately and then composed on top of all programs. I'd rather have a shell that can automatically pipe the standard error of my programs through a helper tool which can detect names like `ENOENT` and do whatever helpfulness the user wants and the usecase demands. Meanwhile, the error strings which perror produces don't even do a great job of being human-friendly guidance - new users, and even just busy non-experts in a hurry, benefit from detailed hints, typo-checking, alternative suggestions, and so on.
So when I think about the external error string as an API first, the `errno` name seems essential, and then the `perror` output seems superfluous and worse for parsing.
But I do recognize that the perror output can always be stripped off - if you know the format enough to unambiguously isolate the perror noise from the rest of the error, then yes it's annoying, but it's very surmountable.
I also recognize that in the world we live in right now, users aren't going to run my programs in a shell that automatically translates their errors into something more helpful in a user-configurable way. That doesn't exist yet, I haven't written it yet nor spread the vision, and when we do write it, adoption will lag behind a lot. So when a new user runs a program directly, sees `ENOENT`, and has no idea what it means, it doesn't do them much good for me to say "well this wouldn't be a problem if the rest of your system was set up how I envision". At best I can hope that the error code, combined with my carefully chosen prefixes like "error changing directory", do a good job of getting them to the right answer through Google.
So I find myself on the fence... I want to do the third option - the third option is the best architecture, and the future of CLI is to be even more of an API and less of a UI - but I know the second option would be more helpful for a decent amount of real users right now.