This commit is contained in:
Raven Scott
2026-04-03 19:49:21 -04:00
parent 76dc3ecb7c
commit 4dfee8398b
19 changed files with 504 additions and 250 deletions
+7 -10
View File
@@ -2,17 +2,10 @@
"name": "find",
"section": 1,
"title": "find files",
"synopsis": [
"find [PATH...] [EXPRESSION]"
],
"description": "Walks directories and applies expressions (-name, -type, -print, -maxdepth, logical -and/-or/-not).",
"synopsis": ["find [PATH...] [EXPRESSION]"],
"description": "Walks directories and applies expressions (-name, -type, -print, -maxdepth, -mindepth, logical -and/-or/-not).",
"options": [],
"keywords": [
"find",
"directory",
"walk",
"search"
],
"keywords": ["find", "directory", "walk", "search"],
"bareOsNotes": "Expression syntax is a simplified subset.",
"examples": [
{
@@ -27,6 +20,10 @@
"caption": "max depth",
"code": "find . -maxdepth 2 -type f"
},
{
"caption": "skip top directory level (GNU-like -mindepth 2)",
"code": "find . -mindepth 2 -type f"
},
{
"caption": "OR names",
"code": "find . \\( -name \"*.c\" -o -name \"*.h\" \\)"
+23 -5
View File
@@ -1,4 +1,8 @@
async function walk(ctx, dir, nameRe, wantType, maxDepth, curDepth) {
/**
* curDepth is distance from the search root directory (0 at the initial path).
* Printed paths use gnu-like depth curDepth + 1 (immediate children of the root are depth 1).
*/
async function walk(ctx, dir, nameRe, wantType, maxDepth, minDepth, curDepth) {
if (maxDepth >= 0 && curDepth > maxDepth) return
let names
try {
@@ -16,15 +20,21 @@ async function walk(ctx, dir, nameRe, wantType, maxDepth, curDepth) {
continue
}
if (!st) continue
const gnuDepth = curDepth + 1
if (!nameRe || nameRe.test(n)) {
if (!wantType || st.type === wantType) ctx.console.log(path)
if (!wantType || st.type === wantType) {
if (gnuDepth >= minDepth) ctx.console.log(path)
}
}
if (st.type === 'directory') await walk(ctx, path, nameRe, wantType, maxDepth, curDepth + 1)
if (st.type === 'directory')
await walk(ctx, path, nameRe, wantType, maxDepth, minDepth, curDepth + 1)
}
}
async function run(ctx, argv) {
let maxDepth = -1
/** Minimum path depth below the search root (1 = default; same as GNU -mindepth 1 for tree walks). */
let minDepth = 1
/** @type {string | null} */
let nameGlob = null
/** @type {'file' | 'directory' | 'symlink' | null} */
@@ -36,6 +46,11 @@ async function run(ctx, argv) {
maxDepth = Number.parseInt(argv[++i], 10)
continue
}
if (a === '-mindepth' && argv[i + 1]) {
minDepth = Number.parseInt(argv[++i], 10)
if (!Number.isFinite(minDepth) || minDepth < 1) minDepth = 1
continue
}
if (a === '-name' && argv[i + 1]) {
nameGlob = argv[++i]
continue
@@ -61,9 +76,12 @@ async function run(ctx, argv) {
const root = rest[0] || '.'
let nameRe = null
if (nameGlob) {
const esc = nameGlob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.')
const esc = nameGlob
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
.replace(/\*/g, '.*')
.replace(/\?/g, '.')
nameRe = new RegExp('^' + esc + '$')
}
const abs = ctx.vfs.resolveLogical(root)
await walk(ctx, abs, nameRe, wantType, maxDepth, 0)
await walk(ctx, abs, nameRe, wantType, maxDepth, minDepth, 0)
}