With --verbose, chgrp and chown emit their informational per-file lines (changed ownership of …, ownership of … retained as …, group of … retained as …) to stderr, prefixed with the utility name (chgrp: /chown: ). GNU emits these to stdout with no prefix. They are normal informational output, not errors.
$ tmp=$(mktemp -d); touch "$tmp/f"
# uutils: line goes to stderr, with a "chgrp:" prefix
$ chgrp --verbose "$(id -gn)" "$tmp/f" 1>/dev/null
chgrp: group of '/tmp/…/f' retained as youruser
# GNU: line goes to stdout, no prefix
$ /usr/bin/chgrp --verbose "$(id -gn)" "$tmp/f" 2>/dev/null
group of '/tmp/…/f' retained as youruser
Consequences
- Redirection is broken.
chgrp --verbose grp file > log.txt leaves log.txt empty in uutils (the lines leak to the terminal via stderr); GNU writes them into log.txt.
- Spurious prefix on informational lines.
- Exit-code divergence on a failing stdout. Because the lines go to stderr, redirecting stdout to a failing sink doesn't surface a write error:
$ chgrp --verbose "$(id -gn)" "$tmp/f" > /dev/full ; echo "exit=$?"
exit=0 # uutils
# GNU writes to stdout -> ENOSPC -> exit=1
Root cause
The verbose informational lines are produced by report_ownership_change_success in src/uucore/src/lib/features/perms.rs, which uses the show_error! macro. show_error! always writes to stderr and prepends util_name(), so it's the wrong sink for informational output.
With
--verbose,chgrpandchownemit their informational per-file lines (changed ownership of …,ownership of … retained as …,group of … retained as …) to stderr, prefixed with the utility name (chgrp:/chown:). GNU emits these to stdout with no prefix. They are normal informational output, not errors.Consequences
chgrp --verbose grp file > log.txtleaveslog.txtempty in uutils (the lines leak to the terminal via stderr); GNU writes them intolog.txt.Root cause
The verbose informational lines are produced by
report_ownership_change_successinsrc/uucore/src/lib/features/perms.rs, which uses theshow_error!macro.show_error!always writes to stderr and prependsutil_name(), so it's the wrong sink for informational output.