Environment:
rsync version 3.2.7, protocol version 32
Description:
rsync documents (and implements) a general convention where many boolean long options can be turned off by prefixing them with --no- (e.g. --no-owner, --no-motd, --no-recursive, --no-human-readable). However, this does not hold for --dry-run/-n: appending --no-dry-run after --dry-run/-n on the same command line is accepted without any parse error, but does not cancel dry-run mode — the command continues to behave exactly as if --no-dry-run had not been given.
Additionally, a malformed variant with a doubled dash, --no--dry-run, is also accepted without error and is likewise a no-op — suggesting the --no- prefix handling performs loose/generic matching rather than validating that a genuine negatable pair exists for the given option before accepting the input.
Steps to reproduce:
- rsync -avz --dry-run --no-dry-run source/ dest/
Expected: dry-run is cancelled; files are actually transferred.
Actual: dry-run remains in effect; nothing is transferred; no error is printed.
- rsync -avz --dry-run --no--dry-run source/ dest/
Same result as (1): silently accepted, dry-run remains active.
Root cause (from options.c, current source):
--dry-run is registered as:
{"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
Unlike options that have genuine negatable pairs explicitly registered, e.g.:
{"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
{"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
there is no "no-dry-run" table entry anywhere in options.c. The option is a plain POPT_ARG_NONE flag with no registered negation. Whatever mechanism accepts --no-dry-run without a parse error is therefore matching/stripping the --no- prefix generically without verifying a corresponding negation actually exists for the target option, and silently doing nothing instead of either applying a real negation or rejecting the input as an unknown option.
Expected behavior (either would resolve this):
(a) Implement a genuine --no-dry-run that clears the dry_run flag, consistent with rsync's own general --no- convention users would reasonably expect it to follow, or
(b) Reject --no-dry-run (and other --no-X variants with no registered negation) as an unrecognized option, rather than silently accepting and ignoring it — silent acceptance-without-effect is a worse outcome than a clear error, since it can mislead a user into believing dry-run has been cancelled when it has not.
Impact:
Low severity (workaround is trivial — just omit --dry-run rather than trying to cancel it), but worth fixing given rsync's strong general reputation for precise, well-defined option semantics — this is a real inconsistency in that story, and could plausibly mislead a user into believing a real transfer occurred when it was actually still a dry run.
Environment:
rsync version 3.2.7, protocol version 32
Description:
rsync documents (and implements) a general convention where many boolean long options can be turned off by prefixing them with --no- (e.g. --no-owner, --no-motd, --no-recursive, --no-human-readable). However, this does not hold for --dry-run/-n: appending --no-dry-run after --dry-run/-n on the same command line is accepted without any parse error, but does not cancel dry-run mode — the command continues to behave exactly as if --no-dry-run had not been given.
Additionally, a malformed variant with a doubled dash, --no--dry-run, is also accepted without error and is likewise a no-op — suggesting the --no- prefix handling performs loose/generic matching rather than validating that a genuine negatable pair exists for the given option before accepting the input.
Steps to reproduce:
Expected: dry-run is cancelled; files are actually transferred.
Actual: dry-run remains in effect; nothing is transferred; no error is printed.
Same result as (1): silently accepted, dry-run remains active.
Root cause (from options.c, current source):
--dry-run is registered as:
{"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
Unlike options that have genuine negatable pairs explicitly registered, e.g.:
{"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
{"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
there is no "no-dry-run" table entry anywhere in options.c. The option is a plain POPT_ARG_NONE flag with no registered negation. Whatever mechanism accepts --no-dry-run without a parse error is therefore matching/stripping the --no- prefix generically without verifying a corresponding negation actually exists for the target option, and silently doing nothing instead of either applying a real negation or rejecting the input as an unknown option.
Expected behavior (either would resolve this):
(a) Implement a genuine --no-dry-run that clears the dry_run flag, consistent with rsync's own general --no- convention users would reasonably expect it to follow, or
(b) Reject --no-dry-run (and other --no-X variants with no registered negation) as an unrecognized option, rather than silently accepting and ignoring it — silent acceptance-without-effect is a worse outcome than a clear error, since it can mislead a user into believing dry-run has been cancelled when it has not.
Impact:
Low severity (workaround is trivial — just omit --dry-run rather than trying to cancel it), but worth fixing given rsync's strong general reputation for precise, well-defined option semantics — this is a real inconsistency in that story, and could plausibly mislead a user into believing a real transfer occurred when it was actually still a dry run.