“如何使用JavaScript编写Shell脚本”是我们最新的JavaScript新闻通讯的社论,您可以在此处订阅 。
这周我不得不升级客户的网站以使用SSL。 这本身并不是一项艰巨的任务-安装证书只是单击一个按钮-但是一旦我进行了切换,就留下了很多混合内容警告 。 修复这些问题的一部分意味着,我必须浏览主题目录(这是一个WordPress网站),并确定所有通过HTTP包含资产的文件。
以前,我会使用一个小的Ruby脚本来自动执行此操作。 Ruby是我学习的第一门编程语言,非常适合此类任务。 但是,我们最近发表了一篇有关使用Node创建命令行界面的文章 。 这篇文章提醒我,JavaScript早已超越了浏览器,并且可以(除其他外)用于桌面脚本。
在本文的其余部分中,我将说明如何使用JavaScript递归地遍历目录中的文件并识别出现的任何指定字符串。 我还将简要介绍如何使用JavaScript编写Shell脚本,并带您逐步编写自己的脚本。
这里唯一的先决条件是Node.js。 如果您尚未安装此文件,则可以转到其网站并下载二进制文件之一 。 或者,您可以使用版本管理器,例如nvm。 我们在这里有一个教程。
那么从哪里开始呢? 我们需要做的第一件事是遍历主题目录中的所有文件。 幸运的是,Node的本机文件系统模块带有一个readdir方法,我们可以使用该方法。 它以目录路径和回调函数为参数。 回调有两个参数( err
和entries
),其中entries
是目录中entries
名称的数组,不包括.
和..
—分别是当前目录和父目录。
const fs = require('fs');
function buildTree(startPath) {
fs.readdir(startPath, (err, entries) => {
console.log(entries);
});
}
buildTree('/home/jim/Desktop/theme');
如果要遵循此步骤,请将以上内容保存在名为search_and_replace.js
的文件中,并使用node search_and_replace.js
从命令行运行它。 您还需要将路径调整到所使用的目录。
到目前为止,一切都很好! 上面的脚本将目录的顶级条目记录到控制台,但是我的主题文件夹包含子目录,这些子目录也包含需要处理的文件。 这意味着我们需要遍历条目数组,并让函数针对遇到的任何目录调用自身。
为此,如果要处理目录,首先需要确定。 幸运的是,文件系统模块也有一个方法: lstatSync 。 这将返回一个fs.Stats对象,该对象本身具有isDirectory
方法。 此方法相应地返回true
或false
。
请注意,这里我们使用的是lstat
的同步版本。 这对于一次性脚本来说很好,但是如果性能很重要,则应首选异步版本。
const fs = require('fs');
function buildTree(startPath) {
fs.readdir(startPath, (err, entries) => {
console.log(entries);
entries.forEach((file) => {
const path = `${startPath}/${file}`;
if (fs.lstatSync(path).isDirectory()) {
buildTree(path);
}
});
});
}
buildTree('/home/jim/Desktop/theme');
如果运行该脚本,现在将看到它为当前目录及其包含的每个子目录打印文件和文件夹的列表。 成功!
接下来,我们需要添加一些逻辑来识别任何PHP文件,打开它们并在其中搜索我们要查找的任何字符串。 这可以通过使用简单的正则表达式来检查以“ .php”结尾的文件名来完成,然后在满足该条件的情况下调用processFile
函数,并将当前路径作为参数传递给它。
让我们对路径名的构造方式进行一些小的改进。 到现在为止,我们一直在使用字符串插值,但是由于正斜杠,这只能在Unix环境中使用。 但是,Node的路径模块提供了join方法 ,该方法将考虑分隔符。
const fs = require('fs');
const Path = require('path');
function processFile(path) {
console.log(path);
}
function buildTree(startPath) {
fs.readdir(startPath, (err, entries) => {
entries.forEach((file) => {
const path = Path.join(startPath, file);
if (fs.lstatSync(path).isDirectory()) {
buildTree(path);
} else if (file.match(/\.php$/)) {
processFile(path);
}
});
});
}
buildTree('/home/jim/Desktop/theme');
如果您在此时运行脚本,它应该递归目录树并打印出可能找到的任何php文件的路径。
剩下要做的就是打开脚本找到的文件并进行处理。 这可以使用Node的readFileSync方法完成,该方法接受文件路径及其编码(可选)作为参数。 如果指定了编码,则此函数返回一个字符串。 否则,它将返回一个缓冲区。
现在,我们可以将文件的内容读取到一个变量中,然后可以在每个换行符上进行拆分并遍历结果数组。 然后,使用JavaScript的match方法查找所需的单词或短语是一个简单的问题:
function processFile(path) {
const text = fs.readFileSync(path, 'utf8');
text.split(/\r?\n/).forEach((line) => {
if (line.match('http:\/\/')) {
console.log(line.replace(/^\s+/, ''));
console.log(`${path}\n`);
}
});
}
如果您现在运行脚本,它将打印出找到匹配项的每一行以及文件名。
在我的特定情况下,这就足够了。 该脚本吐出了少量的“ http”,我可以手动修复。 任务完成! 但是,使用replace()
和fs.writeFileSync更改每次出现的情况并将新内容写回到文件中来自动化该过程将很简单。 您还可以使用child_process.exec打开Sublime中的文件以供编辑:
const exec = require('child_process').exec;
...
exec(`subl ${path}`)
这种脚本可以完成很多任务,而不仅仅是处理文本文件。 例如,也许您想批量重命名一堆音乐曲目,或从目录中删除每个Thumbs.db
文件。 也许您想从远程API获取数据,解析CSV文件或即时生成文件。 清单继续...
您还可以将JavaScript文件设置为可执行文件,以便在单击它们时运行它们。 Axel Rauschmayer在他的文章中谈到了这一点。 通过Node.js用JavaScript编写Shell脚本 。
我们终于得到它了。 我已经演示了如何使用JavaScript遍历目录树并处理其中包含的文件的子集。 这是一个简单的示例,但是它强调了JavaScript可用于浏览器之外的全部任务这一点,其中桌面脚本就是其中之一。
现在结束了。 您是否使用JavaScript自动执行脚本任务? 如果不是,您是否使用其他偏好的语言,或者您是bash纯粹主义者? 您可以自动执行哪些任务? 在下面的评论中让我知道。
From: https://www.sitepoint.com/shell-scripts-javascript/