前言
在现代前端开发中,脚手架工具扮演着至关重要的角色。它们不仅提高了开发效率,而且通过为项目提供标准化和一致的开发模式,使得团队协作变得更加无缝和高效。Node.js,作为一个强大的JavaScript运行环境,使得编写和使用这些脚手架工具成为可能。
目标:
- 脚手架的基本原理即执行流程
- 完成仿 ls -al 查询文件的创建脚手架开发流程
什么是脚手架?
- 操作系统的可执行文件,可以通过c、c++、Java、JavaScript(Node.js)等各种语言编写
- 通过控制台命令即可执行的逻辑
- 前端脚手架是一种工具或框架,主要用于自动化前端项目的设置和配置流程,以便开发者能够迅速启动并运行新的项目,而无需从头开始配置环境、编写样板代码或设置常见的项目结构。
脚手架执行流程
在创建vue 或者React项目时,通过官网提供的脚手架即可快速的搭建项目,很好奇,为什么在控制台输入简单的命令即可快速生成呢?
我们可以通过查看 Vue 的脚手架方式来一探究竟
这里需要理解几个基本概念
-
环境变量(相当于操作系统级别的全局变量)
-
软连接(相当于windows系统的快捷方式)
-
这里 vue、which 本质都是脚手架
-
通过 vim 打开文件,就是vue脚手架的执行文件
其中!user/bin/env node 是将当前文件在node环境中跑
脚手架执行原理如下:
- 在终端输入 vue-create-app
- 终端解析出 vue 命令
- 终端在环境变量中找到 vue 命令
- 终端根据 vue 命令链接到实际 vue.js
- 终端利用 node 执行 vue.js
- vue.js 解析 command / options
- vue.js 执行command
- 执行完毕,退出执行
脚手架开发入门
脚手架开发流程
创建 npm 项目
javascript
npm init -y
创建 /bin/index.js 脚手架入口文件,最上方添加:操作系统按照node来解析文件
javascript
#!/usr/bin/env node
console.log("我们来开发脚手架!");
配置 package.json ,添加 bin 属性
javascript
{
// 指定名称
"name": "tianyu.ls",
"version": "1.0.0",
"description": "",
"main": "index.js",
// bin 指定 名称 以及 文件路径 在index.js中即可编写脚手架代码
"bin":{
"tianyu-ls":"./bin/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
创建软连接
- 首先需要 运行 npm link 将此文件的路径添加到全部变量,也就是对全局进行一个开发测试
javascript
npm link
- 执行 ~ which node 命令 获取node所在地址
- 进入到上一级 bin目录,并 通过 ll 命令 查看当前文件下所有的文件,当前创建的文件也就在这里,后面的地址就是指向实际的路径
- 此时就可以直接通过 bin中设置的软链接进行本地任意文件访问,如果其他电脑需要访问,还需要npm进行发布
在项目中访问
在桌面也可以进行访问
编写脚手架代码
在开始之前,如果对 ls 以及 Unix文件权限不了解的可以先链接至
认识 Unix(Linux、MacOS)文件权限系统及文件类型
有个简单的概念后再进行编写 查看文件功能
目标:
tianyu-a tianyu-a-l tianyu-al 的方式 实现系统 ls 查看文件功能
以下是系统ls使用方式 输入ls 将所有的文件进行依次展示
javascript
➜ tianyu.ls git:(main) ✗ ls
bin package.json
输入 ls-l 以列表方式进行展示
javascript
➜ tianyu.ls git:(main) ✗ ls -l
total 8
drwxr-xr-x 3 wangtianyu staff 96 4 11 21:20 bin
-rw-r--r-- 1 wangtianyu staff 271 4 11 21:20 package.json
输入 ls-a 显示所有文件
javascript
➜ tianyu.ls git:(main) ✗ ls -a
. .. bin package.json
输入 ls -a-l 会将列和文件同时展示,也就是相当于 同时输入了 ls-l ls-a 这一块的含义 链接:认识 Unix(Linux、MacOS)文件权限系统及文件类型
javascript
➜ tianyu.ls git:(main) ✗ ls -a -l
total 8
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:20 .
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:19 ..
drwxr-xr-x 3 wangtianyu staff 96 4 11 21:20 bin
-rw-r--r-- 1 wangtianyu staff 271 4 11 21:20 package.json
输入 ls -al 等价于输入了 ls-a-l
javascript
➜ tianyu.ls git:(main) ✗ ls -al
total 8
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:20 .
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:19 ..
drwxr-xr-x 3 wangtianyu staff 96 4 11 21:20 bin
获取控制台输入的参数 process.argv
在创建vue项目的时候 是输入 vue create app 同时输入了三个命令,这个demo也是类似 有-a -l,那么应该应该如何获取呢? 使用node.js中的 **process.argv **
javascript
console.log(process.argv);
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls
[
'/Users/wangtianyu/.nvm/versions/node/v19.9.0/bin/node',
'/Users/wangtianyu/.nvm/versions/node/v19.9.0/bin/tianyu-ls'
]
如果 输入 ls-a-l
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -a -l
[
'/Users/wangtianyu/.nvm/versions/node/v19.9.0/bin/node',
'/Users/wangtianyu/.nvm/versions/node/v19.9.0/bin/tianyu-ls',
'-a',
'-l'
]
定义parseArgs函数
👆🏻已经获取到参数,那么接下来就可以判断,哪一个输入哪一个没有输入来进行具体的操作
javascript
function parseArgs() {
let isAll = false;
let isList = false;
const args = process.argv.slice(2);
args.forEach((arg) => {
console.log(arg, "66");
if (arg.includes("a")) {
isAll = true;
}
if (arg.includes("l")) {
isList = true;
}
});
return {
isAll,
isList,
};
}
module.exports = parseArgs;
javascript
#!/usr/bin/env node
const parseArgs = require("./parseArgs");
const { isAll, isList } = parseArgs();
console.log({
isAll,
isList,
});
控制台看下打印尝试各种情况均正常
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -l
-l 66
{ isAll: false, isList: true }
➜ tianyu.ls git:(main) ✗ tianyu-ls -a -l
-a 66
-l 66
{ isAll: true, isList: true }
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
-al 66
{ isAll: true, isList: true }
➜ tianyu.ls git:(main) ✗ tianyu-ls -a
-a 66
{ isAll: true, isList: false }
➜ tianyu.ls git:(main) ✗
完成只输入ls的情况 获取所有的文件 但是不包含隐藏文件
- 也就是 isAll and is isList 都是false的情况,并排除.开头的文件,也就是隐藏文件
- process.cwd() 获取当前工作目录文件夹
- fs.readdirSync(dir) 当前文件夹下的所有文件,并返回一个文件名数组
实现代码
javascript
#!/usr/bin/env node
const fs = require("fs");
const parseArgs = require("./parseArgs");
const { isAll, isList } = parseArgs();
// 当前工作目录:
const dir = process.cwd(); // 获取当前工作目录,并赋值给dir
// 没有输入 -a -l 时
if (!isAll && !isList) {
// 遍历当前文件夹下的所有文件,排除以.开头的文件或文件夹
let files = fs.readdirSync(dir);
console.log(files, "files");
files = files.filter((file) => !file.startsWith("."));
let output = "";
// 遍历所有文件
files.forEach((file) => (output += file + " "));
console.log(output);
}
console.log({
isAll,
isList,
});
尝试获取项目文件目录
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls
[ 'bin', 'package.json' ] files
bin package.json
{ isAll: false, isList: false }
完成 ls -a 的情况,获取所有文件包含隐藏文件
- 在目录下创建一个以 · 开头的文件 例如 .log
javascript
#!/usr/bin/env node
const fs = require("fs");
const parseArgs = require("./parseArgs");
// console.log("我们来开发脚手架!");
// console.log(process.argv);
const { isAll, isList } = parseArgs();
// 当前工作目录:
const dir = process.cwd(); // 获取当前工作目录,并赋值给dir
let files = fs.readdirSync(dir);
let output = "";
// 没有输入 -a -l 时 获取当前文件夹下的所有文件排除以.开头的文件或文件夹(隐藏文件)
if (!isAll && !isList) {
// 遍历当前文件夹下的所有文件,排除以.开头的文件或文件夹
files = files.filter((file) => !file.startsWith("."));
// 遍历所有文件
files.forEach((file) => (output += file + " "));
}
// 输入 -a 时 获取所有文件
else if (isAll && !isList) {
files.forEach((file) => (output += file + " "));
}
console.log(output);
console.log({
isAll,
isList,
});
尝试获取项目的文件 创建的 .log 就可以被打印出来了
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -a
-a 66
.log bin package.json
{ isAll: true, isList: false }
同终端测试其他文件夹也是可以的,例如我的桌面
javascript
~ tianyu-ls -a
.CFUserTextEncoding .DS_Store .Huawei .SwitchHosts .Trash .WhistleAppData .altrc .android .aws .bash_history .bash_prefile .bash_prefile.swp .bash_sessions .bytertc .cache .chat .config .degit .gitconfig .hvigor .lesshst .lingma .local .node_repl_history .npm .npmrc .nvm .oh-my-zsh .ohpm .pm2 .pnpm-state .snipaste .sogouinput .ssh .startingAppData .swp .viminfo .vscode .vuerc .yarn .yarnrc .zcompdump .zcompdump-wangtianyu-5.9 .zcompdump-wangtianyu-5.9.zwc .zprofile .zsh_history .zsh_sessions .zshrc .zshrc.pre-oh-my-zsh .zshrc.pre-oh-my-zsh-2023-11-19_02-34-30 .zshrc.save .zshrc.swl .zshrc.swm .zshrc.swn .zshrc.swo .zshrc.swp Applications Desktop Documents Downloads Library Movies Music Pictures Postman Public Sunlogin Files \ debug-test localhost-key.pem localhost.pem package-lock.json yarn-error.log yarn.lock
{ isAll: true, isList: false }
完成 ls -a -l 的情况,获取文件列表包含权限创建时间等
javascript
#!/usr/bin/env node
const fs = require("fs");
const parseArgs = require("./parseArgs");
// console.log("我们来开发脚手架!");
// console.log(process.argv);
const { isAll, isList } = parseArgs();
// 当前工作目录:
const dir = process.cwd(); // 获取当前工作目录,并赋值给dir
let files = fs.readdirSync(dir);
let output = "";
// 没有输入 -a -l 时 获取当前文件夹下的所有文件排除以.开头的文件或文件夹(隐藏文件)
if (!isAll) {
files = files.filter((file) => !file.startsWith("."));
}
// ls -a的情况 获取当前文件夹下的所有文件包含隐藏文件
if (!isList) {
files.forEach((file) => (output += file + " "));
}
// ls -a -l的情况 已列表形式展示当前文件夹下的所有文件包含隐藏文件
else {
const total = `total ${files.length}` + "\n";
files.forEach((file, index) => {
// 这里是优化最后一行不带空行的逻辑
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
// 第一行显示文件总数量
output = total + output;
}
console.log(output);
console.log({
isAll,
isList,
});
测试
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -a -l
total 3
.log
bin
package.json
{ isAll: true, isList: true }
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
total 3
.log
bin
package.json
{ isAll: true, isList: true }
比较系统ls -al 去烧了文件权限等信息,所以下一步就是Nodejs如何获取系统文件类型和文件权限
javascript
tianyu.ls git:(main) ✗ ls -a -l
// 文件数量
total 8
// 权限 //数量 // 用户名 // 分组 //大小 // 最新修改日期
drwxr-xr-x 5 wangtianyu staff 160 4 13 12:29 .
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:19 ..
-rw-r--r-- 1 wangtianyu staff 0 4 13 12:29 .log
drwxr-xr-x 4 wangtianyu staff 128 4 11 21:59 bin
理解文件存储的表达方式
javascript
理解文件存储的表达方式
unix使用32位二进制数存储文件类型和权限
0000 0000 0000 0000
0000(文件类型) 000(特殊权限) 000(用户权限) 000(组权限) 000(其他权限)
0
1
10
11
110
111
1110
1111
当前类型位:0001 如何判断最后一位是1呢?
0001 & 0001 = 0001 true
0000 & 0001 = 0000 false
0000 & 0010 = 0000 false
因为 只要带0 结果都是false 所以所有带0的都要删除
所以最终能够表达状态的只有
1
11
111
1111
所以 例如 用户权限这里 001 010 100 都可以代表一种权限
在nodejs中 可以通过 fs.statSync(file) 的方式获取
javascript
else {
const total = `total ${files.length}` + "\n";
files.forEach((file, index) => {
const mode = fs.statSync(file);
console.log(mode);
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
output = total + output;
}
// 结果
Stats {
dev: 16777234,
mode: 33188, // 文件十进制
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 22456782,
size: 0,
blocks: 0,
atimeMs: 1712982550363.2869,
mtimeMs: 1712982550363.2869,
ctimeMs: 1712982550363.2869,
birthtimeMs: 1712982550363.2869,
atime: 2024-04-13T04:29:10.363Z,
mtime: 2024-04-13T04:29:10.363Z,
ctime: 2024-04-13T04:29:10.363Z,
birthtime: 2024-04-13T04:29:10.363Z
}
**可以通过利用网页工具将十进制转化成二进制 ** f16877 =》 0100 0001 1110 1101 那么0100 就是对应表格中的 文件夹
判断文件类型
判断是否为文件夹
javascript
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const isDirectory = stat.isDirectory(); // 是否是文件夹
console.log(mode, isDirectory);
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
javascript
=> tianyu.ls git:(main) ✗ tianyu-ls -al
33188 false
16877 true
33188 false
total 3
.log
bin
package.json
{ isAll: true, isList: true }
这里 true 就代表是文件夹 false即不是文件夹,那么isDirectory它是如何实现的呢?下面就是原理部分:
isDirectory原理
这里可以再进行优化 通过 fs.constants.S_IFDIR 获取文件的二进制
javascript
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const isDirectory = stat.isDirectory(); // 是否是文件夹
const isDir = mode & fs.constants.S_IFDIR; // mode 和 fs.constants.S_IFDIR是否相同 通过 & 来判断是否是文件夹
console.log(mode, isDirectory, isDir, fs.constants.S_IFDIR);
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
33188 false 0 16384
16877 true 16384 16384
33188 false 0 16384
total 3
.log
bin
package.json
{ isAll: true, isList: true }
通过这里观察查看 isDir 如果大于0 就是一个文件夹
javascript
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const isDirectory = stat.isDirectory(); // 判断文件类型1:直接利用API方式判断是是文件夹
const isDir = mode & fs.constants.S_IFDIR; // 判断文件类型2:mode 和 fs.constants.S_IFDIR是否相同 通过 & 来判断是否是文件夹
console.log(mode, isDir > 0, fs.constants.S_IFDIR);
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
33188 false 16384 // .log
16877 true 16384 // bin
33188 false 16384 // package.json
total 3
.log
bin
package.json
{{isAll: true, isList: true }
其中 bin 是一个文件夹 十进制是 16384 > 0
其余的都不是一个文件夹 ,下图中也是正确的 我们把 16384 转化成 二进制 : 100 0000 0000 0000
javascript
bin 16877 0100 0001 1110 1101
S_IFDIR 16384 0100 0000 0000 0000
// 因为 0100 都相同 那么说明 bin文件就是一个文件夹
以上就是判断文件夹的方式
判断文件类型
这里通过 mode & fs.constants.S_IFREG; // 是否是文件 来判断
javascript
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const isDirectory = stat.isDirectory(); // 判断文件类型1:直接利用API方式判断是是文件夹
const isDir = mode & fs.constants.S_IFDIR; // 判断文件类型2:mode 和 fs.constants.S_IFDIR是否相同 通过 & 来判断是否是文件夹
const isFile = mode & fs.constants.S_IFREG; // 是否是文件
console.log(mode, isFile > 0, fs.constants.S_IFDIR);
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
33188 true 16384
16877 false 16384
33188 true 16384
total 3
.log
bin
package.json
{ isAll: true, isList: true }
所以 在 drwxr-xr-x 中 第一位 例如d 就可以通过这种方式判断出来了 通过 mode & fs.constants.对应的文件类型 > 0 来判断结果 这里再整理一下文件信息
javascript
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const isDirectory = stat.isDirectory(); // 是否是文件夹
const isDir = mode & fs.constants.S_IFDIR; // mode 和 fs.constants.S_IFDIR是否相同 通过 & 来判断是否是文件夹
const isFile = mode & fs.constants.S_IFREG; // 是否是文件
console.log({
文件名称: file,
文件十进制: mode,
// 二进制这里手动将mode十进制到网站转化器中进行展示
是否是文件夹: isDir > 0,
是否是文件: isFile > 0,
});
index === files.length - 1 ? (output += file) : (output += file + "\n");
});
结果 + 手动加上二进制 就是
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
{ '文件名称': '.log', '文件十进制': 33188, '文件二进制':1000 0001 1010 0100,'是否是文件夹': false, '是否是文件': true }
{ '文件名称': 'bin', '文件十进制': 16877, '文件二进制':100 0001 1110 1101,'是否是文件夹': true, '是否是文件': false }
{ '文件名称': 'package.json', '文件二进制': 33188,'文件二进制':1000 0001 1010 0100,'是否是文件夹': false,'是否是文件': true}
总结:编写函数
mode & fs.constants.文件类型 是否为true
javascript
const fs = require("fs");
function getFileType(mode) {
let fileType = "";
const isDir = mode & (fs.constants.S_IFDIR === fs.constants.S_IFDIR); // 是否是文件夹
const isFile = mode & (fs.constants.S_IFREG === fs.constants.S_IFREG); // 是否是文件
const isLink = mode & (fs.constants.S_IFLNK === fs.constants.S_IFLNK); // 是否是链接
if (isDir) {
fileType = "d";
} else if (isFile) {
fileType = "-";
} else if (isLink) {
fileType = "l";
}
return fileType;
}
module.exports = getFileType;
判断文件权限
分析
可以通过这个图来查看,首先 file type已经观察过了, 通过 mode & fs.constants.对应的文件类型 > 0
- 拿 0100 0001 1110 1101 bin 这个文件夹举例子
javascript
0100 0001 1110 1101 转化一下缩进就是
0100 000 111 101 101
d(文件夹类型) rwx r-x r-x
减去缩进 drwxr-xr-x
再进行对比 ls
drwxr-xr-x 5 wangtianyu staff 160 4 13 14:29 bin
通过上图的结果 对比发现 一模一样
- 再拿 1000 0001 1010 0100 package举例子
javascript
1000 0001 1010 0100 转化缩进
1000 000 110 100 100
-(文件类型) rw- r-- r--
再对比 ls
-rw-r--r-- 1 wangtianyu staff 271 4 11 21:20 package.json
通过两个例子找规律 已经实现了对文件的权限读取,接下来就是用如何判断文件类型以及文件权限通过node的方式进行对应字符转化
编写函数
javascript
/**
* 理解文件存储的表达方式
* unix使用32位二进制数存储文件类型和权限
* 0000 0000 0000 0000
* 0000(文件类型) 000(特殊权限) 000(用户权限) 000(组权限) 000(其他权限)
*
* 0
* 1
* 10
* 11
* 110
* 111
* 1110
* 1111
* 当前类型位:0001 如何判断最后一位是1呢?
* 0001 & 0001 = 0001 true
* 0000 & 0001 = 0000 false
* 0000 & 0010 = 0000 false
* 因为 只要带0 结果都是false 所以所有带0的都要删除
*
* 所以最终能够表达状态的只有
* 1
* 11
* 111
* 1111
*
* 所以 例如 用户权限这里 001 010 100 都可以代表一种权限
*/
const fs = require("fs");
function auth(mode) {
let authString = "";
// u: 当前登录用户
const canUserRead = mode & fs.constants.S_IRUSR;
const canUserWrite = mode & fs.constants.S_IWUSR;
const canUserExecute = mode & fs.constants.S_IXUSR;
canUserRead ? (authString += "r") : (authString += "-");
canUserWrite ? (authString += "w") : (authString += "-");
canUserExecute ? (authString += "x") : (authString += "-");
// g: 当前用户所在分组
const canGroupRead = mode & fs.constants.S_IRGRP;
const canGroupWrite = mode & fs.constants.S_IWGRP;
const canGroupExecute = mode & fs.constants.S_IXGRP;
canGroupRead ? (authString += "r") : (authString += "-");
canGroupWrite ? (authString += "w") : (authString += "-");
canGroupExecute ? (authString += "x") : (authString += "-");
// o:其他用户
const canOtherRead = mode & fs.constants.S_IROTH;
const canOtherWrite = mode & fs.constants.S_IWOTH;
const canOtherExecute = mode & fs.constants.S_IXOTH;
canOtherRead ? (authString += "r") : (authString += "-");
canOtherWrite ? (authString += "w") : (authString += "-");
canOtherExecute ? (authString += "x") : (authString += "-");
return authString;
}
module.exports = auth;
引入文件类型及权限函数并做对比
javascript
#!/usr/bin/env node
const fs = require("fs");
const parseArgs = require("./parseArgs");
// console.log("我们来开发脚手架!");
// console.log(process.argv);
const { isAll, isList } = parseArgs();
const authFn = require("./auth");
const getFileType = require("./getFileType");
// 当前工作目录:
const dir = process.cwd(); // 获取当前工作目录,并赋值给dir
let files = fs.readdirSync(dir);
let output = "";
// 没有输入 -a -l 时 获取当前文件夹下的所有文件排除以.开头的文件或文件夹(隐藏文件)
if (!isAll) {
files = files.filter((file) => !file.startsWith("."));
}
// ls -a的情况 获取当前文件夹下的所有文件包含隐藏文件
if (!isList) {
files.forEach((file) => (output += file + " "));
}
// ls -a -l的情况 已列表形式展示当前文件夹下的所有文件包含隐藏文件
else {
const total = `total ${files.length}` + "\n";
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const authString = authFn(mode); // 权限
const fileType = getFileType(mode); // 文件类型
// console.log({
// authString,
// 文件名称: file,
// 文件十进制: mode,
// 是否是文件夹: isDir > 0,
// 是否是文件: isFile > 0,
// });
// 拼接字符串并去掉最后行空行
index === files.length - 1
? (output += fileType + authString + "\t" + file)
: (output += fileType + authString + "\t" + file + "\n");
});
output = total + output;
}
console.log(output);
console.log({
isAll,
isList,
});
查看对比结果
基本摸一样,其中文件大小 文件名称等都可以从 fs.statSync(file) 文件信息对象中获取,而计算机用户也就是这里的wangtianyu 不能获得,所以下一步先获取 主机名称
javascript
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
total 3
-rw-r--r-- .log
drwxr-xr-x bin
-rw-r--r-- package.json
➜ tianyu.ls git:(main) ✗ ls -al
total 3
-rw-r--r-- 1 wangtianyu staff 0 4 13 12:29 .log
drwxr-xr-x 6 wangtianyu staff 192 4 13 16:25 bin
-rw-r--r-- 1 wangtianyu staff 271 4 11 21:20 package.json
获取主机名称
javascript
// child_process 模块提供了衍生子进程的能力,我们可以通过 execSync 方法来执行 shell 命令,并获取其返回值。
const cp = require("child_process");
function getFileUser(state) {
const { uid } = state;
const username = cp.execSync(`id -nu ${uid}`).toString().trim();
const groupname = cp.execSync(`id -ng ${uid}`).toString().trim();
return username + "\t" + groupname + "\t";
}
module.exports = getFileUser;
function getFileUser(state) {
const { uid } = state;
const username = cp.execSync(`id -nu ${uid}`).toString().trim();
const groupname = cp.execSync(`id -ng ${uid}`).toString().trim();
return username + "\t" + groupname + "\t";
}
module.exports = getFileUser;
获取创建时间
javascript
const dayjs = require("dayjs");
function getFileSizeAndDate(stat) {
const { size, birthtimeMs } = stat;
const date = dayjs(birthtimeMs).format("MMM D HH:mm");
return size + "\t" + date + "\t";
}
module.exports = getFileSizeAndDate;
获取子文件夹数量
javascript
const fs = require("fs");
function getSubFileCount(stat, file) {
console.log({
stat,
file,
});
if (file === ".Trash") {
return "1\t"; // 如果是垃圾箱,直接返回1
}
if (!stat.isDirectory() || !fs.readdirSync(file).length) {
return "1\t"; // 如果不是目录,直接返回1
}
// 读取目录下的所有文件,包含 . ------ 指向当前目录自身的引用。 .. ------ 指向父目录的引用。
const allEntries = Number(fs.readdirSync(file).length) + 2;
return allEntries.toString().trim() + "\t";
}
module.exports = getSubFileCount;
自测 + 主文件代码
javascript
➜ tianyu.ls git:(main) ✗ ls -al
total 8
-rw-r--r-- 1 wangtianyu staff 0 4 13 12:29 .log
drwxr-xr-x 9 wangtianyu staff 288 4 13 17:53 bin
-rw-r--r-- 1 wangtianyu staff 322 4 13 17:38 package.json
➜ tianyu.ls git:(main) ✗ tianyu-ls -al
total 3
-rw-r--r-- 1 wangtianyu staff 0 4 13 12:29 .log
drwxr-xr-x 9 wangtianyu staff 288 4 11 21:20 bin
-rw-r--r-- 1 wangtianyu staff 322 4 11 21:19 package.json
javascript
#!/usr/bin/env node
const fs = require("fs");
const parseArgs = require("./parseArgs");
// console.log("我们来开发脚手架!");
// console.log(process.argv);
const { isAll, isList } = parseArgs();
const authFn = require("./auth");
const getFileType = require("./getFileType");
const getFileUser = require("./getFileUser");
const getFileSizeAndDate = require("./getFileSizeAndDate");
const getSubFileCount = require("./getSubFileCount");
// 当前工作目录:
const dir = process.cwd(); // 获取当前工作目录,并赋值给dir
let files = fs.readdirSync(dir);
let output = "";
// 没有输入 -a -l 时 获取当前文件夹下的所有文件排除以.开头的文件或文件夹(隐藏文件)
if (!isAll) {
files = files.filter((file) => !file.startsWith("."));
}
// ls -a的情况 获取当前文件夹下的所有文件包含隐藏文件
if (!isList) {
files.forEach((file) => (output += file + " "));
}
// ls -a -l的情况 已列表形式展示当前文件夹下的所有文件包含隐藏文件
else {
const total = `total ${files.length}` + "\n";
files.forEach((file, index) => {
const stat = fs.statSync(file); // 文件信息对象
const mode = stat.mode; // 文件二进制
const authString = authFn(mode); // 文件权限
const fileType = getFileType(mode); // 文件类型
const fileUser = getFileUser(stat); // 获取文件用户即用户分组
const sizeAndDate = getFileSizeAndDate(stat); //大小和日期
const subDirCount = getSubFileCount(stat, file); // 子文件数量
index === files.length - 1
? (output +=
fileType +
authString +
subDirCount +
fileUser +
"\t" +
sizeAndDate +
file)
: (output +=
fileType +
authString +
subDirCount +
fileUser +
"\t" +
sizeAndDate +
file +
"\n");
});
// console.log('Contents of "bin" directory:', fs.readdirSync("./bin"));
output = total + output;
}
console.log(output);
console.log({
isAll,
isList,
});
至此完成了 模仿 ls -al 查看文件及列表的功能,接下去还需要进打包以及npm发布的过程,未完待续...