本文将深入探讨Deno,并创建一个命令行工具,用于在文件和文件夹中搜索文本。我们将使用Deno提供的各种API方法来读取和写入文件系统。
上一篇文章中,我们使用Deno构建了一个命令行工具,用于向第三方API发出请求。本文中,我们将暂时忽略网络操作,构建一个工具,允许您在当前目录内的文件和文件夹中搜索文本——类似于grep之类的工具。
注意:我们构建的工具不会像grep那样优化和高效,我们也并非旨在取代它!构建这样一个工具的目的是为了熟悉Deno的文件系统API。
Deno.readDir
用于列出文件和Deno.readTextFile
用于读取文件内容,从而简化文件系统交互,无需额外导入。path.join
等函数,用于连接文件路径。–allow-read
或–allow-write
才能执行文件系统操作,通过控制脚本功能来增强安全性。deno compile
将Deno脚本编译成独立的可执行文件,通过封装必要的权限来简化分发和执行。我们假设您已经在本地机器上运行了Deno。您可以查看Deno网站或上一篇文章,以获取更详细的安装说明,以及如何为您的编辑器添加Deno支持的信息。
在撰写本文时,Deno的最新稳定版本是1.10.2,这就是我在本文中使用的版本。
作为参考,您可以在GitHub上找到本文的完整代码。
与上一篇文章一样,我们将使用Yargs来构建用户可以用来执行我们工具的界面。让我们创建index.ts并用以下内容填充它:
<code class="language-typescript">import yargs from "https://deno.land/x/yargs@v17.0.1-deno/deno.ts"; interface Yargs<argvreturntype></argvreturntype> { describe: (param: string, description: string) => Yargs<argvreturntype>; </argvreturntype> demandOption: (required: string[]) => Yargs<argvreturntype>; </argvreturntype> argv: ArgvReturnType; } interface UserArguments { text: string; } const userArguments: UserArguments = (yargs(Deno.args) as unknown as Yargs<userarguments>) </userarguments> .describe("text", "the text to search for within the current directory") .demandOption(["text"]) .argv; console.log(userArguments); </code>
这里有很多值得指出的内容:
我们可以使用deno run index.ts运行它并查看我们的Yargs输出:
<code class="language-typescript">import yargs from "https://deno.land/x/yargs@v17.0.1-deno/deno.ts"; interface Yargs<argvreturntype></argvreturntype> { describe: (param: string, description: string) => Yargs<argvreturntype>; </argvreturntype> demandOption: (required: string[]) => Yargs<argvreturntype>; </argvreturntype> argv: ArgvReturnType; } interface UserArguments { text: string; } const userArguments: UserArguments = (yargs(Deno.args) as unknown as Yargs<userarguments>) </userarguments> .describe("text", "the text to search for within the current directory") .demandOption(["text"]) .argv; console.log(userArguments); </code>
现在是时候开始实现了!
在我们开始搜索给定文件中的文本之前,我们需要生成一个要搜索的目录和文件列表。Deno提供Deno.readdir,它是“内置”库的一部分,这意味着您不必导入它。它在全局命名空间中可用。
Deno.readdir是异步的,并返回当前目录中文件和文件夹的列表。它将这些项目作为AsyncIterator返回,这意味着我们必须使用for await ... of循环来获取结果:
<code class="language-bash">$ deno run index.ts Check file:///home/jack/git/deno-file-search/index.ts Options: --help Show help [boolean] --version Show version number [boolean] --text the text to search for within the current directory [required] Missing required argument: text</code>
这段代码将从当前工作目录(Deno.cwd()提供)读取并记录每个结果。但是,如果您现在尝试运行脚本,您将收到错误:
<code class="language-typescript">for await (const fileOrFolder of Deno.readDir(Deno.cwd())) { console.log(fileOrFolder); } </code>
请记住,Deno要求所有脚本都明确地获得从文件系统读取的权限。在我们的例子中,--allow-read
标志将使我们的代码能够运行:
<code class="language-bash">$ deno run index.ts --text='foo' error: Uncaught PermissionDenied: Requires read access to <cwd>, run again with the --allow-read flag </cwd>for await (const fileOrFolder of Deno.readDir(Deno.cwd())) { ^ at deno:core/core.js:86:46 at unwrapOpResult (deno:core/core.js:106:13) at Object.opSync (deno:core/core.js:120:12) at Object.cwd (deno:runtime/js/30_fs.js:57:17) at file:///home/jack/git/deno-file-search/index.ts:19:52</code>
在本例中,我在构建工具的目录中运行脚本,因此它找到了TS源代码、.git存储库和.vscode文件夹。让我们开始编写一些函数来递归地导航此结构,因为我们需要找到目录中的所有文件,而不仅仅是顶级文件。此外,我们可以添加一些常见的忽略项。我认为没有人会希望脚本搜索整个.git文件夹!
在下面的代码中,我们创建了getFilesList函数,它接受一个目录并返回该目录中的所有文件。如果遇到目录,它将递归调用自身以查找任何嵌套的文件,并返回结果:
<code class="language-bash">~/$ deno run --allow-read index.ts --text='foo' { name: ".git", isFile: false, isDirectory: true, isSymlink: false } { name: ".vscode", isFile: false, isDirectory: true, isSymlink: false } { name: "index.ts", isFile: true, isDirectory: false, isSymlink: false } </code>
然后我们可以像这样使用它:
<code class="language-typescript">const IGNORED_DIRECTORIES = new Set([".git"]); async function getFilesList( directory: string, ): Promise<string> { const foundFiles: string[] = []; for await (const fileOrFolder of Deno.readDir(directory)) { if (fileOrFolder.isDirectory) { if (IGNORED_DIRECTORIES.has(fileOrFolder.name)) { // Skip this folder, it's in the ignore list. continue; } // If it's not ignored, recurse and search this folder for files. const nestedFiles = await getFilesList( path.join(directory, fileOrFolder.name), ); foundFiles.push(...nestedFiles); } else { // We found a file, so store it. foundFiles.push(path.join(directory, fileOrFolder.name)); } } return foundFiles; } </string></code>
我们还得到一些看起来不错的输出:
<code class="language-typescript">const files = await getFilesList(Deno.cwd()); console.log(files); </code>
我们现在可以使用模板字符串组合文件路径,如下所示:
<code class="language-bash">$ deno run --allow-read index.ts --text='foo' [ "/home/jack/git/deno-file-search/.vscode/settings.json", "/home/jack/git/deno-file-search/index.ts" ] </code>
但这使用Deno的path模块会更好。此模块是Deno作为其标准库一部分提供的模块之一(与Node使用其path模块非常相似),如果您使用过Node的path模块,则代码看起来非常相似。在撰写本文时,Deno提供的最新标准库版本是0.97.0,我们从mod.ts文件导入path模块:
<code class="language-typescript">import yargs from "https://deno.land/x/yargs@v17.0.1-deno/deno.ts"; interface Yargs<argvreturntype></argvreturntype> { describe: (param: string, description: string) => Yargs<argvreturntype>; </argvreturntype> demandOption: (required: string[]) => Yargs<argvreturntype>; </argvreturntype> argv: ArgvReturnType; } interface UserArguments { text: string; } const userArguments: UserArguments = (yargs(Deno.args) as unknown as Yargs<userarguments>) </userarguments> .describe("text", "the text to search for within the current directory") .demandOption(["text"]) .argv; console.log(userArguments); </code>
mod.ts在导入Deno的标准模块时始终是入口点。此模块的文档位于Deno网站上,并列出了path.join,它将接受多个路径并将它们连接成一个路径。让我们导入并使用该函数,而不是手动组合它们:
...(此处省略了其余代码,因为与原文重复,并且已经在上一个输出中进行了修改和优化。)
...(The rest of the code is omitted because it's repetitive from the original and has already been modified and optimized in the previous output.)
以上是在DENO中使用文件系统的详细内容。更多信息请关注PHP中文网其他相关文章!