功能描述
1、使用 node.js 监听文本文件。
2、监听到文件改变后创建子进程。
3、在子进程中执行系统命令。
问题描述
1、windows 环境下,监听过程中,报错 “spawn dir ENOENT”。
2、windows 环境下,最终系统命令输出的结果乱码。
主要代码
'use strict' //开启严格模式
/* 引入 node.js 的 fs 模块 */
const fs = require('fs');
/* 引入 node.js 的 child_process 模块下的 spawn 方法 */
/* 因为本例中只使用到 spawn 方法, 所以建议只引入使用到的方法 */
const spawn = require('child_process').spawn;
//或者 import { spawn } from 'child_process';
/* 访问命令行输入的参数。参数为数组类型 */
/* 在本例中,索引[0]为 node 的绝对路径,[1]为该 node.js 文件的绝对路径, [2]为手动指定的目标文件名路径 */
const filename = process.argv[2];
if(!filename){
throw Error('A file to watch must be specified !');
}
/* 开启监听 */
/* watch 方法需要两个参数。1、需要监听的目标文件路径;2、监听的回调方法 */
/* 本例中,回调方法使用箭头函数。() 表示空参 */
fs.watch(filename, () => {
/* spawn 方法执行系统命令 */
/* 该方法有三个参数,后两个参数为可选参数。1、执行的命令;2、参数数组;3、配置信息对象 */
//const ls = spawn('ls', ['-l', '-h', filename]);//linux 下的命令。相当于 ls -l -h filename
const ls = spawn('dir', ['-w', filename]);//windows 下的命令,相当于 dir /w filename
/* spawn 方法返回的对象是 ChildProcess。其 stdin、stdout、stderr 属性都是 Stream ,可以直接输出 */
/* 使用 pipe 方法将子进程中的输出内容传送到标准输出流 */
ls.stdout.pipe(process.stdout);
});
console.log(`Now watching ${filename} for changed...`);
调试过程
1、创建监听目标文件 target.txt;
2、保存上述代码到同目录下 watcher.js
3、以 windows 环境为例,打开 cmd 命令行,执行如下命令,发现报错。
F:\NodeJS\filesystem> node watcher.js target.txt
Now watching target.txt for changed...
events.js:182
throw er; // Unhandled 'error' event
^
Error: spawn dir ENOENT
at exports._errnoException (util.js:1026:11)
at Process.ChildProcess._handle.onexit (internal/child_process.js:189:19)
at onErrorNT (internal/child_process.js:366:16)
at _combinedTickCallback (internal/process/next_tick.js:102:11)
at process._tickCallback (internal/process/next_tick.js:161:9)
报错原因分析:
错误提示:ENOENT。
其一般为:No such file was found or the specified path name doesn’t exist 的错写。
即:没有找到文件,或者指定的路径名不存在。
一般,执行 spawn 命令时遇到该报错,最常见的原因有两个:
1、参数不匹配。当前命令下没有这个参数。
2、运行环境发生变化时,当前环境下不存在这个命令。
检查一遍,似乎发现上述代码现在是在 windows 环境下运行,执行的是 dir /w 命令
似乎并没有问题。
但是,忽略了一点。在 windows 环境下,cmd 命令也是需要显示的去指派的。
也就是说,我们需要先调用 cmd 下的 dir 命令: spawn(‘cmd’, [‘dir’])
在较新的版本的 node.js 中,可以直接配置 shell:true 属性来控制是否调用 shell。
/* 即将上面的代码换成如下:增加一个 shell 的配置项。其值可以直接写为 true */
/* 但在本例中,令其值为 (当前环境是否为 windows 环境?) 的一个 bool 值 */
const ls = spawn('dir', ['-w', filename], {shell:process.platform === 'win32'});
再次运行 node 调用该 js 文件
F:\NodeJS\filesystem> node watcher.js target.txt
Now watching target.txt for changed...
������ F �еľ��� code
�������� F2A6-190E
F:\NodeJS\filesystem ��Ŀ¼target.txt
1 ���ļ� 37 �ֽ�
0 ��Ŀ¼ 209,791,561,728 �����ֽ�
������ F �еľ��� code
�������� F2A6-190E
乱码的问题在 cmd 中已经不少见了。
老规矩,先确认 cmd 的编码规则。运行如下命令
F:\NodeJS\filesystem>chcp
活动代码页: 936
可以看到,936:GBK。也是 cmd 的默认编码。而 node.js 默认输出的是 UTF-8
修改 cmd 的编码为 UTF-8
F:\NodeJS\filesystem>chcp 65001
Active code page: 65001
重新运行 watcher.js 启用监听
F:\NodeJS\filesystem>node watcher.js target.txt
Now watching target.txt for changed...
Volume in drive F is code
Volume Serial Number is F2A6-190E
Directory of F:\NodeJS\filesystem
target.txt
1 File(s) 39 bytes
0 Dir(s) 209,791,561,728 bytes free
Volume in drive F is code
Volume Serial Number is F2A6-190E
乱码问题已经没有了。
但需要注意的是,在 cmd 中修改编码仅对当前窗口有效,下次打开 cmd 需要重新设置。
如果想 cmd 保持某一编码,需要在设置完编码后,打开属性 --> 选项 --> 勾选"丢弃旧的副本"选项。