美文网首页
读<了不起的Node.js>-06.命令行工具(CLI)以及FS

读<了不起的Node.js>-06.命令行工具(CLI)以及FS

作者: 在路上的海贼 | 来源:发表于2018-08-04 16:42 被阅读0次

    简介

    • nodejs中重要的API:处理进程(stdio)的stdin以及stdout相关的API还有文件系统FS的相关api
    • 之前介绍过 node通过使用回调和事件机制来实现并发,现在这些api 会首次接触到基于非阻塞事件的io编程中的流控制
    • 搭建一个简单的命令行文件浏览器,功能是允许用户读取和创建文件

    需求

    -定义需求

    • 程序需要在命令行运行,意味着程序得通过node命令执行,或者直接执行,然后通过终端提供交互给用户进行输入和输出

    • 启动后显示当前目录下列表

    • 选择文件时,显示文件的内容

    • 选择目录时,显示目录下信息

    • 运行结束后退出

    • 根据需求我们需要做到以下几点

      • 创建模块
      • 决定采用同步的fs 还是异步的fs
      • 理解什么是流 stream
      • 实现输入输出
      • 重构
      • 使用fs进行文件交互
      • 完成

    编写首个 node程序

    创建模块

    • 先创建文件夹 file-xeplorer
    • 在内部创建 package.json

    /这里版本号遵循semver 的版本控制标准/

    {
      "name": "file-explorer",
      
      "version": "0.0.1",
      "description": "一个命令行文件资源管理器",
      "dependencies": {}
    }
    
    • 通过命令行输入 npm install 来验证package.json是否有效
    • 接着创建index.js

    同步还是异步

    • 我们从生命依赖关系开始,由于stdio Api 是一个全局的process对象的一部分,所以我们这里为一个依赖就是fs
    /*
    * 模块依赖
    * */
    const fs = require('fs');
    //同步版本
     console.log(fs.readdirSync('.'));
    

    [图片上传失败...(image-3124cc-1533372113516)]

    • index.js
    /*
    * 模块依赖
    * */
    const fs = require('fs');
    
    fs.readdir(__dirname, (err, files) =>{
        console.log(files);
    });
    

    什么是流 stream

    console.log('hello world');
    console.log('-----------------');//换行
    process.stdout.write('hello world');
    process.stdout.write('------------');//不换行
    
    • process全局对象包含了三个流对象

      • stdin : 标准输入
      • stdout :标准输出
      • stderr : 标准错误
        [图片上传失败...(image-dca6c7-1533372113516)]
    • 第一个stdin 是一个可读流,而stdout和stderr都是可写流

    • stdin流默认的状态是暂停的,通常,执行一个程序,程序会做一些处理,然后退出, 在这里程序需要纸质处在运行状态来接收用户输入的数据

    • 当恢复那个流的时候,node会观察对应的文件描述(unix状态下为0),随后宝石时间循环的运行,同时保持程序不退出,等待事件的触发,除非有io等待,否则nodejs总是会自动退出

    输入和输出

    • 这里我们写出第一部分,列出当前目标路下的问文件 然后等待用户输入
    /*
    * 模块依赖
    * */
    const fs = require('fs');
    
    fs.readdir(process.cwd(), function (err, files) {
        console.log('');
    
        if (!files.length) {
            return console.log('   \033[31m 没有文件显示 !\033[39m\n');
        }
        
        console.log('    选择您想要查看的文件或目录\n');
    
        function file(i) {
            let filename = files[i];
    
            fs.stat(__dirname + '/' + filename, function (err, stat) {
                if (stat.isDirectory()) {
                    console.log('    ' + i + ' \033[36m' + filename + '/\033[39m');
                } else {
                    console.log('    ' + i + ' \033[36m' + filename + '\033[39m');
                }
                i++;
                if (i == files.length) {
                    console.log('');
                    process.stdout.write('    \033[33m输入你的选择: \033[39m');
                    process.stdin.resume();
                    process.stdin.serEncoding('utf8');
                } else {
                    file(i)
                }
            });
        }
        file(0);
    });
    
    
    • 下面我们来分析上面的代码
    • 为了输出更加友好我们首先输出一个空行:console.log('')
    • 如果files数组为空,告知用户当前目录没有文件 文本周围的\033[31m\033[39m为了让文本呈现为红色,例子中得\n也是为了输出友好console.log(' \033[31m 没有文件显示 !\033[39m\n')
    • 在输出查看语句后
    • 定义了一个函数,数组中每个元素都会执行这个额函数,这里也出现贯穿始终的异步流程控制模式,出阿航执行,
    • function file(i){....}
    • 然后,现货区文件名,在查看文件名对应的路径情况,fs.stat会个提出文件或者目录的元数据let filename = files[i];fs.stat(__dirname + '/' + filename, function (err, stat)..
    • 回调函数中,同时还给出了错误对象和一个stat对象,背离中使用到的stat对象上的方法是isDirectory
    • 如果路径所代表的是目录,我们就用有别于文件的颜色表示出来
    • 下面就是流控制中得核心部分了
      -计数器不断递增,同时检查是否还有为处理的文件
    • 如果所有文件都处理完了 此时提示用户进行选择,注意
    • 这里用的是process.stdout.weite而不是cl,这样就无需换行可以直接在提示语后面输入
    • 这里process.stdin.resume()就是等待用户输入
    • 后面是设置流编码为utf8
    • 如果还有未处理的文件则用递归调用函数进行处理
    • 直到列出所有文件,用户输入完毕,紧接着进行下一步串行处理
    • 重要模式: 串行处理

    重构

    • 要做重构,我们从创建快捷变量开始
    • [图片上传失败...(image-6e8c61-1533372113516)]
    • 由于我们书写的代码是异步,会有问题,随着韩数量的增长,过多的函数嵌套会让程序的可读性变差
    • 为此我们可以为每一个异步操作预先定一个一个函数
    • 抽取函数
    • [图片上传失败...(image-85c6-1533372113516)]
    • [图片上传失败...(image-582698-1533372113516)]
    • 读取用户输入后,接下来要做的就是根据用户输入做出相应处理,用户需要选择要读取的文件,所以代码层,我们设置了stdin的编码后,开始监听器data时间:
    function read() {
            console.log('');
            stdout.write('    \033[33m输入你的选择: \033[39m');
            stdin.resume();
            stdin.setEncoding('utf8');
            stdin.on('data', option);
        }
    
        function option(data) {
            if (!files[Number(data)]) {
                stdout.write('    \031[33m输入你的选择: \033[39m')
            } else {
                stdin.pause();
            }
        }
    
    • 这里我们检查用户的输入是否匹配files数组的下表,files数组是fs.readdir回调函数中的一部分,上述代码中,我们将utf-8编码字符串转换为了Number类型来方便检查
    • 现在我们已经能定位文件了那就开始读取他
        function option(data) {
            const filename = files[Number(data)];
            if (!filename) {
                stdout.write('    \033[33m输入你的选择: \033[39m')
            } else {
                stdin.pause();
                fs.readFile(__dirname + '/' + filename, 'utf8', function (err, data) {
                    console.log('');
                    console.log('\033[90m'+data.replace(/(.*)/g, '    $1')+'\033[39m');
                });
            }
        }
    
    
    • 这样我们已经可以读取一个文件了
    • 但是读取文件夹的时候就会出错,在这种情况下,我们就将其目录下的文件列表显示出来,
    • 为了避免再次执行fs.stat 我们在file函数中,将stat对象保存下来

    至此我们就完成了首个查看文件和文件夹内容的程序,虽然很简陋

    /*
    * 模块依赖
    * */
    const fs = require('fs'), stdin = process.stdin, stdout = process.stdout;
    
    
    fs.readdir(process.cwd(), function (err, files) {
        console.log('');
    
        if (!files.length) {
            return console.log('   \033[31m 没有文件显示 !\033[39m\n');
        }
    
        console.log('    选择您想要查看的文件或目录\n');
        let stats = [];
    
        function file(i) {
            let filename = files[i];
    
            fs.stat(__dirname + '/' + filename, function (err, stat) {
                stats[i] = stat;
                if (stat.isDirectory()) {
                    console.log('    ' + i + ' \033[36m' + filename + '/\033[39m');
                } else {
                    console.log('    ' + i + ' \033[36m' + filename + '\033[39m');
                }
    
                if (++i === files.length) {
                    read();
                } else {
                    file(i)
                }
            });
        }
    
        function read() {
            console.log('');
            stdout.write('    \033[33m输入你的选择: \033[39m');
            stdin.resume();
            // stdin.setEncoding('utf8');
            stdin.on('data', option);
        }
    
        function option(data) {
            const filename = files[Number(data)];
            if (!filename) {
                stdout.write('    \033[33m输入你的选择: \033[39m')
            } else {
                stdin.pause();
                if (stats[Number(data)].isDirectory()) {
                    fs.readdir(__dirname + '/' + filename, function (err, files) {
                        console.log('');
                        console.log('   (' + files.length + '  files)');
                        files.forEach(function (file) {
                            console.log('    -  ' + file);
                        });
                        console.log('');
                    });
                } else {
                    fs.readFile(__dirname + '/' + filename, 'utf8', function (err, data) {
                        console.log('');
                        console.log('\033[90m' + data.replace(/(.*)/g, '    $1') + '\033[39m');
                    });
                }
            }
        }
    
        file(0);
    });
    
    
        
    
    
        
    
    
    
    

    相关文章

      网友评论

          本文标题:读<了不起的Node.js>-06.命令行工具(CLI)以及FS

          本文链接:https://www.haomeiwen.com/subject/kxumvftx.html