coreutils
coreutils copied to clipboard
Usage errors from clap produce exit status 2 but GNU programs produce exit status 1
This is the same issue I reported previously in #2951, but I noticed that this is a general problem with all programs. The problem is that usage errors that arise from argument parsing (for example, due to conflicting options or missing arguments) in uutils programs cause the process to terminate with exit status 2. But in GNU programs, the process terminates with exit status 1.
For example,
GNU head:
$ head --lines
head: option '--lines' requires an argument
Try 'head --help' for more information.
$ echo $?
1
uutils head:
$ ./target/debug/head --lines
error: The argument '--lines <[-]NUM>' requires a value but none was supplied
USAGE:
head [FLAG]... [FILE]...
For more information try --help
$ echo $?
2
If there is a uniform solution that could be applied to all programs, that would likely help with a lot of test cases in the GNU test suite. For example, there are several test cases in tests/misc/uniq.pl that expect exit code 1 where we currently return exit code 2.
I think we should ask clap for some customization for this. If they don't want to add something for this, I would suggest that we use try_get_matches_of with ? and provide Into<UResult> for ClapError, where we set the exit code to 1.
I have opened https://github.com/clap-rs/clap/issues/3426 on the clap repository.
All right, that issue was (understandably) a wontfix for them, so we'll have to go with a custom solution. To expand on my previous suggestion, I think we should implement UResult either directly on the clap error or through a wrapper type and provide a method for conversion (like IO errors). I'm not sure yet.
I think the feature introduced in pull request #3457 provides a way to solve this problem now.
It does! We just have to figure out what all the right exit codes are and use that.
I try to fix all the utils that should return 1 (see below) soon in a PR.
I wrote a little script to find all these cases (please excuse my terrible bash skills 😄)
for path in src/uu/*; do
util=${path#src/uu/};
env $util --definitely-invalid &> /dev/null
gnu_code=$?
cargo run --features unix -- $util --definitely-invalid &> /dev/null
uutils_code=$?
if [ $gnu_code != $uutils_code ]; then
echo $util;
echo "GNU: $gnu_code";
echo "UUTILS: $uutils_code";
fi
done
Below is some summarized output.
# utils that return 2 but should return 1
arch
basename
chgrp
chmod
chown
cksum
comm
csplit
cut
date
dd
df
dircolors
dirname
du
expand
factor
fmt
fold
groups
head
id
install
join
kill
link
ln
logname
mkdir
mkfifo
mknod
mv
nl
nproc
numfmt
od
paste
pathchk
pinky
ptx
pwd
readlink
rm
rmdir
seq
shred
shuf
split
stat
stty
sum
sync
tac
tail
tee
touch
tr
tsort
uname
unexpand
uniq
unlink
uptime
users
wc
who
whoami
yes
# Commands that run other commands and therefore should return 125 to distinguish from errors by the called command
chroot # (already being worked on)
env
nohup
stdbuf
timeout
# and hostname is just being weird?
hostname
GNU: 64
UUTILS: 2