Discussion:
bug#30907: mv return value.
(too old to reply)
Jorgen Harmse
2018-03-22 17:02:59 UTC
Permalink
The documentation specifies the usual exit convention, but doesn’t say what counts as success. For example, I would like
mv -i a b || (diff a b && rm a)
to rename a if b does not exist and to remove a if they are identical, but it doesn’t work.

Regards,
Jorgen Harmse

Sam’s Club Technology
Phone 512.633.2226
***@samsclub.com


This e-mail and any files transmitted with it are confidential and intended solely
for the individual or entity to whom they are addressed. If you have received
this e-mail in error, destroy it immediat
Paul Eggert
2018-03-22 18:48:32 UTC
Permalink
Post by Jorgen Harmse
The documentation specifies the usual exit convention, but doesn’t say what counts as success. For example, I would like
mv -i a b || (diff a b && rm a)
to rename a if b does not exist and to remove a if they are identical, but it doesn’t work.
I'm afraid that's a bit vague. What wording change do you suggest?
Paul Eggert
2018-03-22 21:53:51 UTC
Permalink
My preference is a non-zero exit status when mv does not move/rename the file.
Hmm, this might be doable. POSIX says that the mv exit status is 0 if
"All input files were moved successfully" and is >0 if "An error
occurred". With mv -i, if the user declines a move and there is other
file then neither case applies, so mv can yield either 0 or nonzero as
an exit status. Of course someone would have to write the code and (more
importantly) the documentation to do it that way, but I think your
request is a reasonable one.
Eric Blake
2018-03-22 22:00:25 UTC
Permalink
Post by Paul Eggert
My preference is a non-zero exit status when mv does not move/rename the file.
Hmm, this might be doable. POSIX says that the mv exit status is 0 if
"All input files were moved successfully" and is >0 if "An error
occurred". With mv -i, if the user declines a move and there is other
file then neither case applies, so mv can yield either 0 or nonzero as
an exit status. Of course someone would have to write the code and (more
importantly) the documentation to do it that way, but I think your
request is a reasonable one.
I'd argue the opposite: 0 implies success ("I successfully moved all 0
files that you interactively approved for me to move"), while 1 implies
failure ("I failed to move a file, AND printed an error message why").
When the user declines to move a file, we don't print an error message,
so that should NOT be treated as an error, but successfully skipping the
file. We didn't fail to move the file, because we didn't even attempt
to move the file. That is, interactive decisions can only reduce the
set of files to attempt to move, and not be treated as errors on t heir own.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Jorgen Harmse
2018-03-22 22:47:32 UTC
Permalink
I see Eric's point, but it seems to me that my use case is not unusual. (Rename a file unless the target exists, in which case check that they are the same before removing one.) Perhaps the documentation of mv could suggest a solution, e.g.

(ls b &> /dev/null && diff a b > /dev/null && rm a) || mv -n a b

Regards,
Jorgen Harmse

Sam’s Club Technology
Phone 512.633.2226
***@samsclub.com


This e-mail and any files transmitted with it are confidential and intended solely
for the individual or entity to whom they are addressed. If you have received
this e-mail in error, destroy it immediately. Wal-Mart Confidential.
Post by Paul Eggert
My preference is a non-zero exit status when mv does not move/rename
the file.
Hmm, this might be doable. POSIX says that the mv exit status is 0 if
"All input files were moved successfully" and is >0 if "An error
occurred". With mv -i, if the user declines a move and there is other
file then neither case applies, so mv can yield either 0 or nonzero as
an exit status. Of course someone would have to write the code and (more
importantly) the documentation to do it that way, but I think your
request is a reasonable one.
I'd argue the opposite: 0 implies success ("I successfully moved all 0
files that you interactively approved for me to move"), while 1 implies
failure ("I failed to move a file, AND printed an error message why").
When the user declines to move a file, we don't print an error message,
so that should NOT be treated as an error, but successfully skipping the
file. We didn't fail to move the file, because we didn't even attempt
to move the file. That is, interactive decisions can only reduce the
set of files to attempt to move, and not be treated as errors on t heir own.

--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Eric Blake
2018-03-23 13:00:01 UTC
Permalink
Post by Jorgen Harmse
I see Eric's point, but it seems to me that my use case is not unusual. (Rename a file unless the target exists, in which case check that they are the same before removing one.) Perhaps the documentation of mv could suggest a solution, e.g.
(ls b &> /dev/null && diff a b > /dev/null && rm a) || mv -n a b
That's a TOCTTOU race.

It sounds like what you want is close to the kernel's
renameat2(,RENAME_NOREPLACE) semantics, which atomically renames a file
or fails if the destination already exists, then on failure check if the
two files are identical before removing the source.

I don't know if mv exposes RENAME_NOREPLACE semantics yet, but it should
be taught to do so, where such semantics are available.
Post by Jorgen Harmse
This e-mail and any files transmitted with it are confidential and intended solely
This disclaimer is unenforceable on publicly archived mailing lists.
You may want to send from a personal account instead of spamming us with
your employer's legalese when posting to open source lists.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Kamil Dudka
2018-03-23 13:45:50 UTC
Permalink
Post by Eric Blake
I don't know if mv exposes RENAME_NOREPLACE semantics yet, but it should
be taught to do so, where such semantics are available.
mv was recently patched to use renameat2(..., RENAME_NOREPLACE):

http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=v8.29-9-g29baf25aa

Kamil
Assaf Gordon
2018-10-30 02:57:11 UTC
Permalink
tags 30907 fixed
close 30907
stop

(triaging old bugs)
Post by Kamil Dudka
Post by Eric Blake
I don't know if mv exposes RENAME_NOREPLACE semantics yet, but it should
be taught to do so, where such semantics are available.
http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=v8.29-9-g29baf25aa
Give the above, I'm closing this bug.
Discussion can continue by replying to this thread.

-assaf

Jorgen Harmse
2018-03-23 13:41:10 UTC
Permalink
renameat2 looks like what I need. (The TOCTTOU problem did not apply to my most recent use case (and would have been annoying rather than severe), but I see your point.) Given your objection to having mv -i fail in my case, I guess exposing those semantics means adding to the interface:

--interactive-fail
like --interactive, but return non-zero status if the user chooses not to overwrite a file

--no-clobber-fail
like --no-clobber, but return non-zero status if the target exists

Regards,
Jorgen Harmse

Sam’s Club Technology
Phone 512.633.2226
***@samsclub.com


This e-mail and any files transmitted with it are confidential and intended solely
for the individual or entity to whom they are addressed. If you have received
this e-mail in error, destroy it immediately. Wal-Mart Confidential.
Post by Jorgen Harmse
I see Eric's point, but it seems to me that my use case is not unusual. (Rename a file unless the target exists, in which case check that they are the same before removing one.) Perhaps the documentation of mv could suggest a solution, e.g.
(ls b &> /dev/null && diff a b > /dev/null && rm a) || mv -n a b
That's a TOCTTOU race.

It sounds like what you want is close to the kernel's
renameat2(,RENAME_NOREPLACE) semantics, which atomically renames a file
or fails if the destination already exists, then on failure check if the
two files are identical before removing the source.

I don't know if mv exposes RENAME_NOREPLACE semantics yet, but it should
be taught to do so, where such semantics are available.
Post by Jorgen Harmse
This e-mail and any files transmitted with it are confidential and intended solely
This disclaimer is unenforceable on publicly archived mailing lists.
You may want to send from a personal account instead of spamming us with
your employer's legalese when posting to open source lists.

--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Jorgen Harmse
2018-03-23 14:14:39 UTC
Permalink
Thank you. I don't see renameat2 on my system, but this should be good for systems that have it.

Regards,
Jorgen Harmse

Sam’s Club Technology
Phone 512.633.2226
Post by Eric Blake
I don't know if mv exposes RENAME_NOREPLACE semantics yet, but it should
be taught to do so, where such semantics are available.
mv was recently patched to use renameat2(..., RENAME_NOREPLACE):

http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=v8.29
Loading...