命令行工具以及FS API

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2019-11-21

需求:

1、程序需要在命令行运行,通过终端与用户交互

2、程序启动后,需要显示当前目录列表

3、选择某个文件后,程序需要显示该文件的内容。

4、选择一个目录时,程序显示该目录下的信息。

5、运行结束后程序退出。

// index.js
var fs = require('fs');
var stdin = process.stdin;
var stdout = process.stdout;

var stats = []; // 存储读取文件或者目录的元数据
// readdir 异步 - 读取目录
// 读取当前目录 __dirname 表示当前执行文件时该文件在文件系统中所在的目录
fs.readdir(__dirname, function(err, files) {
  // files 是一个数组 包含当前目录下的 文件和目录 的名称
  if(!files.length) {
    return console.log('\033[31m No files to show!\033[39m\n'); // 输出文字呈红色
  }

  console.log('   Select which file or directory want to see\n');

  function file(i) {
    var filename = files[i];
    // stat会给出文件或者目录的元数据
    fs.stat(__dirname + '/' + filename, (err, stat) => {
      stats[i] = stat;
      // 根据是目录还是文件 展示不同的颜色
      if(stat.isDirectory()) {
        console.log('   ' + i +'  \033[36m' + filename + '\033[39m'); // 蓝色
      } else {
        console.log('   ' + i +'  \033[90m' + filename + '\033[39m'); // 灰色
      }

      i++;
      if (i == files.length) { // 读取完成 与用户交互
        read();
      } else {
        file(i); // 递归
      }
    })
  }

  file(0); // 调用一次 file函数会内部递归 读取所有文件

  // 读取用户输入
  function read() {
    console.log('');
    stdout.write('    \033[33mEnter your choice: \033[39m'); // 黄色
    // 标准输入流默认是暂停 (pause) 的,所以必须要调用 process.stdin.resume() 来恢复 (resume) 接收:
    stdin.resume();
    stdin.setEncoding('utf-8'); //设置编码
    stdin.on('data', data => { // 监听用户输入
      var filename = files[Number(data)]
      if(!filename) {
        stdout.write('    \033[31mEnter your choice: \033[39m'); // 红色5 不存在则重新选择 
      } else {
        // 如果检查通过,我们要确保再次将流暂停(回到默认状态),以便于之后做完fs操作后,程序顺利退出。
        stdin.pause();
        
        // 展示
        if(stats[Number(data)].isDirectory()) { // 如果是目录展示目录下的文件
          fs.readdir(__dirname + '/' + filename, (err, files) => {
            console.log('');
            console.log(`(${files.length} files)`); // 目录下的总文件数
            files.forEach(file => { //file 是目录下的文件或目录名称
              console.log('   -   '+ file);  
            })
          })
        } else { // 如果是文件直接展示文件内容
          fs.readFile(__dirname + '/' + filename, 'utf-8', (err, data) => {
            console.log('');
            console.log('\033[90m' + data +  '\033[39m');
          })
        }
      }
    })
  }
})

// process的输入输出
// stdout 标准输出 可写流
// stdin  标准输入 可读流 状态默认是暂停的
// stderr 标准错误 可写流

相关API

argv

process.argv包含了所有Node程序运行时的参数。返回一个数组,第一个元素始终是node所在的目录,第二个元素始终是执行的文件路径,紧接着是命令行后跟着的参数。

// index.js
console.log(process.argv);

// node index.js --name=jack --hello

// [ '/usr/local/bin/node',
//   '/Users/cz/Desktop/Test/index1.js',
//   '--name=jack',
//   '--hello' ]

// 所以获取有用参数常常这么用
process.argv.slice(2);

// 使用yargs模块可以解析参数 第三方包
var argv = require('yargs').argv;
console.log(argv.name) // jack

// 更可以指定参数名的别名 简化命令
var argv = require('yargs').alias('n', 'name').argv;
// 这样 node index.js --name jack 与 node index.js -n jack一致
console.log(argv.n) // jack

工作目录

// 当前js所在的路径:/Users/cz/Desktop/Test/test.js
console.log(__dirname);

console.log(process.cwd());

// /Users/cz/Desktop/Test
// /Users/cz/Desktop

__dirname : 获取执行文件时该文件在文件系统中所在的目录

process.cwd(): 获取当前工作目录, 比如我们在桌面的终端目录下node ./Test/test.js,此时的桌面目录就是工作目录,返回/Users/cz/Desktop

process.chdir(path): 更改工作目录。

环境变量

NODE_ENV="prd" node
> process.env.NODE_ENV
'prd'
> process.env.SHELL
'/bin/bash'

退出

要让一个程序退出,可以调用process.exit()并提供一个退出代码,比如,当错误发生时,要退出程序,通常使用代码1。

console.error('An Error occurred')
process.exit(1);

信号

进程和操作系统进行通信的其中一种方式就是信号,比如,要进程终止,可以发送SIGKILL信号。

发送信号的方法如下。

$ kill -s SIGKILL [process_id]

process对象能够监听信号事件

process.on('SIGKILL', function() {
  console.log('Got a sign')
  process.exit(0);
})

控制输出文本格式

console.log('\033[90m' + data + '\033[39m');

  • \033表示转义序列开始

  • [表示开始颜色设置

  • 90设置当前颜色

  • m表示设置颜色结束

最后结尾的是将颜色设置还原,下一次输出依然是设置前的默认颜色39。

文件流式读取

有些文件太大的话,使用fs.readFile读取不太合适,需要使用流操作,createReadStream是给你一个ReadableStream,你可以监听它的’data’,一点一点儿处理文件,用过的部分会被GC(垃圾回收),所以占内存少。 readFile是把整个文件全部读到内存里。

var stream = fs.createReadStream('files.txt');
var str = '';

stream.on('data', function(chunk){
  str += chunk
});

stream.on('end', function(chunk){
  console.log(str);
});

流式读取,管道操作。

require('http').createServer(function(req, res) {
  res.write(200,{'Content-Type':'image/png'});
  require('fs').createReadStream('image.png').pipe(res);
  // 等同于

  var stream = require('fs').createReadStream('image.png');
  stream.on('data', function(data){
    res.write(data);
  });

  stream.on('end', function(chunk){
    res.end();
  });
})

监视

有人喜欢用一种可以编译为CSS的语言来书写CSS样式,这个时候,就可以使用监视功能,当源文件发生改变时,就将其编译为CSS文件。

var fs = require('fs');

var files = fs.readdirSync(procwss.cwd()); // 同步读取当前工作目录文件

files.forEach(file => {
  // 监听.less后缀的文件
  if(/\.less/.test(file)) {
    fs.watchFile(process.cwd() + '/' + file, function() {
      console.log(file + 'changed!');
      // 执行编译
      // do something 
    })
  }
})