Subject: bug#62572: cp --no-clobber behavior has changed

From: Pádraig Brady <[email-masked]>
Date: Fri, 15 Dec 2023 18:33:00 +0000

On 15/12/2023 15:56, Michael Stone wrote:
> I tend to think this was a serious mistake: it breaks the behavior of
> existing scripts with no deprecation period. A stated advantage is
> better compatibility with freebsd, but I don't understand why that is
> more desirable than compatibility with all deployed gnu/linux systems? I
> also don't think it's sufficient to try to lawyer out by saying that the
> current behavior was undocumented: the previous documentation said that
> -n would "silently do nothing" and that the return code would be zero on
> success. Logically, unless cp fails to "do nothing", it should exit with
> a zero code.
> 
> Such a drastic change in behavior demands a new flag, not a radical
> repurposing of a widely used existing flag.
> 
> I was hoping to see more action on this bug, but that hasn't happened.
> I'm not sure I see a way forward for debian other than reverting to the
> old behavior. I am reluctant to do so as that will likely lead to
> divergent behavior between distributions, but breaking scripts without a
> compelling reason is also not good. I would encourage coreutils to
> reconsider the change and finding a non-breaking way forward.

Yes it's a fair point.
It's an awkward case, and worth discussing.

To summarise:

   coreutils >= 7.1 had -n skip existing in dest (2009)
   coreutils >= 9.2 has -n immediately fail if existing in dest
   coreutils >= 9.3 has --update=none to skip existing in dest

   FreeBSD >= 4.7/macos has -n immediately fail if existing in dest

   bash has noclobber as a file protection mechanism,
   and fails immediately upon trying to overwrite a file.
   This is more consistent with the new coreutils behavior.

I see a reasonable amount of cp -n usage across github:
https://github.com/search?q=/cp+.*+-n+.*/+path:*.sh&type=code

Now it's not clear which behavior these github usages expect,
and the original docs didn't make it clear which behavior to expect.
A quick scan of the github usages also seem mainly to expect
a protection rather than an update use case, so failing
immediately would be the most appropriate action there too.
Also the original coreutils bug report here expected the new behaviour.

So we probably all agree that failing immediately is the
most appropriate / consistent -n behavior,
but GNU had diverged from that so there are about 10 years
of scripts that may expect the silent skip behavior.

Two options I see are:

- Leave as is and fix -n usages that expected the skip behavior
- Deprecate -n entirely and prompt to use --update={fail,none}

Advantages of leaving as is:
We get consistency of "noclobber" behavior across systems / shells.
We fix cases where previously scripts could have proceeded with
stale old files in place.

Disadvantages of leaving as is:
Users expecting the skip behavior, have to change to --update=none.

There is no potential for data loss etc. so it just comes
down to how disruptive it is, or how often -n was used
with the "skip behavior" assumption.

We've not had much push back as of yet,
and my current thinking is it's not that disruptive a change.
So I'd be 55:45 if favor of keeping things as is.

thanks,
Pádraig.