Skip to content

find: implement -ok and -okdir#650

Open
jmr wants to merge 1 commit intouutils:mainfrom
jmr:ok
Open

find: implement -ok and -okdir#650
jmr wants to merge 1 commit intouutils:mainfrom
jmr:ok

Conversation

@jmr
Copy link
Copy Markdown
Contributor

@jmr jmr commented Apr 6, 2026

-ok is like -exec ... ; but prompts the user on stderr before each invocation and only runs the command if the response is affirmative (starts with 'y' or 'Y'). -okdir is the corresponding -execdir variant. Only the ';' terminator is accepted: POSIX does not define -ok ... + and GNU find rejects it.

Response source: GNU find reads from /dev/tty so that the user's answer always comes from the real terminal even when stdin is occupied (e.g. find -files0-from - -ok rm {} \; reads paths from stdin, so responses cannot also come from there). /dev/tty is the POSIX name for a process's controlling terminal and exists on all Unix-like systems. We open /dev/tty only when stdin is itself a terminal (std::io::stdin().is_terminal() == true). When stdin is a pipe or file, we read from stdin directly — matching BSD find and making scripted use (and integration tests via pipe_in()) work naturally without any special environment variable. On Windows, or when /dev/tty cannot be opened, we likewise fall back to stdin.

Implementation: rather than a separate OkMatcher that duplicated SingleExecMatcher's fields, constructor, and exec logic, the interactive prompt is folded into SingleExecMatcher behind an interactive: bool field. SingleExecMatcher::new_interactive() constructs the -ok/-okdir variant; matches() adds a guarded block that builds a GNU-find-style prompt ("< executable arg... >? ") and calls matcher_io.confirm() before executing. If the user declines, the expression is false and the command is not run. This keeps the arg-parsing, path resolution, current_dir logic, and error handling in one place so bug fixes apply to both -exec and -ok.

Dependencies::confirm() trait method: abstracts prompt+read so matchers remain testable without a real terminal; FakeDependencies uses a VecDeque of preset responses.

Tests:

  • Unit tests (exec_unit_tests.rs): confirmed executes command, declined skips command and returns false, confirmed but command fails returns false, -okdir runs in parent dir
  • Parser unit tests (matchers/mod.rs): missing-arg errors, missing semicolon, correct parse, confirm/decline via FakeDependencies
  • Integration tests (test_find.rs): pipe_in() makes stdin a pipe so is_terminal() returns false and responses are read from the pipe; "y"/"Y"/"yes"/" y" run command, "n" and empty response skip command, -okdir runs in parent dir, prompt format verified, missing-semicolon error

Closes #8.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 89.07563% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.70%. Comparing base (5610042) to head (47e7015).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/find/matchers/mod.rs 87.50% 4 Missing and 3 partials ⚠️
src/find/mod.rs 76.92% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #650      +/-   ##
==========================================
- Coverage   91.76%   91.70%   -0.06%     
==========================================
  Files          31       31              
  Lines        6203     6320     +117     
  Branches      328      337       +9     
==========================================
+ Hits         5692     5796     +104     
- Misses        390      398       +8     
- Partials      121      126       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jmr jmr force-pushed the ok branch 2 times, most recently from a7c0193 to 1f5aa51 Compare April 7, 2026 19:59
-ok is like -exec ... ; but prompts the user on stderr before each
invocation and only runs the command if the response is affirmative
(starts with 'y' or 'Y').  -okdir is the corresponding -execdir variant.
Only the ';' terminator is accepted: POSIX does not define -ok ... + and
GNU find rejects it.

Response source: GNU find reads from /dev/tty so that the user's answer
always comes from the real terminal even when stdin is occupied (e.g.
`find -files0-from - -ok rm {} \;` reads paths from stdin, so responses
cannot also come from there).  /dev/tty is the POSIX name for a
process's controlling terminal and exists on all Unix-like systems.  We
open /dev/tty only when stdin is itself a terminal
(std::io::stdin().is_terminal() == true).  When stdin is a pipe or file,
we read from stdin directly — matching BSD find and making scripted use
(and integration tests via pipe_in()) work naturally without any special
environment variable.  On Windows, or when /dev/tty cannot be opened, we
likewise fall back to stdin.

Implementation: rather than a separate OkMatcher that duplicated
SingleExecMatcher's fields, constructor, and exec logic, the interactive
prompt is folded into SingleExecMatcher behind an `interactive: bool`
field.  `SingleExecMatcher::new_interactive()` constructs the -ok/-okdir
variant; `matches()` adds a guarded block that builds a GNU-find-style
prompt ("< executable arg... >? ") and calls `matcher_io.confirm()`
before executing.  If the user declines, the expression is false and the
command is not run.  This keeps the arg-parsing, path resolution,
current_dir logic, and error handling in one place so bug fixes apply to
both -exec and -ok.

Dependencies::confirm() trait method: abstracts prompt+read so matchers
remain testable without a real terminal; FakeDependencies uses a
VecDeque of preset responses.

Tests:
- Unit tests (exec_unit_tests.rs): confirmed executes command, declined
  skips command and returns false, confirmed but command fails returns
  false, -okdir runs in parent dir
- Parser unit tests (matchers/mod.rs): missing-arg errors, missing
  semicolon, correct parse, confirm/decline via FakeDependencies
- Integration tests (test_find.rs): pipe_in() makes stdin a pipe so
  is_terminal() returns false and responses are read from the pipe;
  "y"/"Y"/"yes"/" y" run command, "n" and empty response skip command,
  -okdir runs in parent dir, prompt format verified, missing-semicolon
  error

Closes uutils#8.
@jmr
Copy link
Copy Markdown
Contributor Author

jmr commented Apr 8, 2026

The code missing in the failing coverage checks looks similar to existing code where there is no failure case possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FeatureComplete: implement ok[dir] predicate

1 participant