一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 前言
三 Node 编写 bash 脚本的解决方案
四 编程前置
五 敞开端口
六 删除文件/文件夹
七 Git 操作
 7.1 工作中罕用 Git 指令
 7.2 切换分支
八 总结
九 参考文献

二 前言

明天 jsliang 在工作中又爽了一把,开明了 VIP 通道:

  1. 主动下载 Excel 文件
  2. 拷贝到指定目录
  3. 执行多语言导入操作
  4. 将导入的资源 git push 下来

尽管看上去粗略的操作是这样,没有什么可讲的。

然而在操作中的时候,可能就比拟繁琐了,例如单单导入资源后,进行的 git 操作:

  1. 增加暂存区:git add .
  2. 切换分支:git checkout -b <branch>
  3. 提交本地版本库:git commit -m "feat: 「多语言」新资源 #master_0720"
  4. 提交近程分支:git push --set-upstream origin <branch>

当然,不仅仅是这个,你提交前必定还得校验下你的多语言资源增加完后,构建是否能顺利通过……


所以,叨了那么多,其实单纯是为了引出 bash 指令的操作。

在工作中,你可能会碰到:

  • Git 系列操作
  • 敞开被占用的零碎端口
  • 删除指定文件/文件夹等
  • ……

然而,对于这些操作你可能一时半会又遗记它的指令,或者它的指令太繁琐了,所以 jsliang 感觉将这些内容装起来,岂不省事?

三 Node 编写 bash 脚本的解决方案

其实对于这个解决方案,jsliang 还是嫌麻烦,所以间接上了 ShellJS

  • ShellJS - Unix shell commands for Node.js

如果小伙伴感觉这样间接上计划有点唐突,心愿有个参考比照,能够看:

  • Node.js 写 bash 脚本终极计划

作者比对了 Node 自带的 child_process API、ShellJSzx,最终采取了 zx 的计划。

当然,jsliang 工作中用的还是 ShellJS,不想那么累再摸索同类库了,所以就装置 ShellJS 吧~

  • 装置:npm i shelljs
  • 装置 TS 编译:npm i @types/shelljs -D

装置结束,开始折腾!

四 编程前置

为了让代码不显得那么又臭又长。

jsliang 写完文章后,通篇浏览 3 次以上,将下文中反复的代码和问题状况给整顿到这里来了,所以小伙伴们拷贝代码前,最初仔细阅读这一章节内容,托付啦~

warn! please read the following carefully

全文目录如下,小伙伴们记得提前新建好目录:

而后反复代码如下:

src/common/index.ts
import { inquirer } from '../base/inquirer';import { Result } from '../base/interface';// 零碎操作import { sortCatalog } from '../base/file/sortDir';import { deleteDir } from '../base/file/deleteDir';// 多语言import { downLoadExcel } from './language/download';import { importLanguage } from './language/import';import { exportLanguage } from './language/export';// shell 操作import { closePort } from '../base/shell/closePort';import { gitCheckout } from '../base/shell/gitCheckout';// 问题记录器const answers = {  q0: '',  q1: '',  q2: '',  q3: '',  q4: '',  q5: '',  q6: '',  q7: '',  q8: '',};const common = (): void => {  // 问题路线:看 questionList.ts  const questionList = [    // q0    {      type: 'list',      message: '请问须要什么服务?',      choices: ['公共服务', '多语言'],    },    // q1    {      type: 'list',      message: '以后公共服务有:',      choices: ['文件排序', '敞开端口', '删除文件夹', 'Git 操作'],    },    // q2    {      type: 'input',      message: '须要排序的文件夹为?(绝对路径)',    },    // q3    {      type: 'list',      message: '请问多语言须要什么反对?',      choices: [        '下载多语言资源',        '导入多语言资源',        '导出多语言资源',      ],    },    // q4    {      type: 'input',      message: '资源下载地址(HTTP)?',      default: 'https://www.kdocs.cn/l/sdwvJUKBzkK2',    },    // q5    {      type: 'input',      message: '你须要敞开的端口是?',    },    // q6    {      type: 'input',      message: '你须要删除的门路是?(全门路)',    },    // q7    {      type: 'list',      message: '请问 Git 须要什么反对?',      choices: [        '切换分支',        // More...      ],    },    // q8    {      type: 'inupt',      message: 'Git 分支名是?',    },  ];  const answerList = [    // q0 - 请问须要什么服务?    async (result: Result, questions: any) => {      answers.q0 = result.answer;      switch (result.answer) {        case '公共服务':          questions[1]();          break;        case '多语言':          questions[3]();          break;        default: break;      }    },    // q1 - 以后公共服务有:    async (result: Result, questions: any) => {      answers.q1 = result.answer;      switch (result.answer) {        case '文件排序': questions[2](); break;        case '敞开端口': questions[5](); break;        case '删除文件夹': questions[6](); break;        case 'Git 操作': questions[7](); break;        default: break;      }    },    // q2 - 须要排序的文件夹为?(绝对路径)    async (result: Result, _questions: any, prompts: any) => {      answers.q2 = result.answer;      const sortResult = await sortCatalog(result.answer);      if (sortResult) {        console.log('排序胜利!');        prompts.complete();      }    },    // q3 - 请问多语言须要什么反对?    async (result: Result, questions: any, prompts: any) => {      answers.q3 = result.answer;      switch (result.answer) {        case '下载多语言资源':        case '导入多语言资源':          questions[4]();          break;        case '导出多语言资源':          const exportResult = await exportLanguage();          if (exportResult) {            console.log('导出胜利!');            prompts.complete();          }        default: break;      }    },    // q4 - 资源下载地址(HTTP)?    async (result: Result) => {      answers.q4 = result.answer;      const download = async (): Promise<any> => {        const downloadResult = await downLoadExcel(result.answer);        if (downloadResult) {          console.log('下载胜利!');          return true;        }      };      switch (answers.q3) {        case '下载多语言资源':          await download();          break;        case '导入多语言资源':          await download();          const importResult = await importLanguage();          if (importResult) {            console.log('导入结束!');          }        default:          break;      }    },    // q5 - 你须要敞开的端口是?    async (result: Result, _questions: any, prompts: any) => {      answers.q5 = result.answer;      const closeResult = await closePort(result.answer);      if (closeResult) {        console.log('敞开胜利');        prompts.complete();      }    },    // q6 - 你须要删除的门路是?(全门路)    async (result: Result, _questions: any, prompts: any) => {      answers.q6 = result.answer;      const deleteResult = await deleteDir(result.answer);      if (deleteResult) {        console.log('删除胜利');        prompts.complete();      }    },    // q7 - 请问 Git 须要什么反对?    async (result: Result, questions: any) => {      answers.q7 = result.answer;      questions[8]();    },    // q8 - Git 分支名是?    async (result: Result, _questions: any, prompts: any) => {      answers.q8 = result.answer;      const checkoutResult = await gitCheckout(result.answer);      if (checkoutResult) {        console.log('切换胜利');        prompts.complete();      }    },  ];  inquirer(questionList, answerList);};export default common;
src/common/questionList.ts
// common 板块的问题征询路线export const questionList = {  '公共服务': { // q0    '文件排序': { // q1      '须要排序的文件夹': 'Work 工作', // q2    },    '敞开端口': { // q1      '须要敞开的端口': 'Work 工作', // q5    },    '删除文件夹': { // q1      '须要删除的门路': 'Work 工作', // q6    },    'Git 操作': { // q1      '切换分支': { // q7        '分支名': 'Work 工作', // q8      },    },  },  '多语言': { // q0    '下载多语言资源': { // q3      '下载地址': 'Work 工作', // q4    },    '导入多语言资源': { // q3      '下载地址': 'Work 工作', // q4    },    '导出多语言资源': { // q3      '导出全量资源': 'Work 工作',      '导出单门资源': 'Work 工作',    }  },};

前面文章表明:

更新 src/common/index.ts

更新 src/common/questionList.ts

这种状况,小伙伴们就不必操作啦,本文有 3 个中央都有这种形容的。

同样,为了防止援用和运行代码的时候,报 TypeScript 谬误,新增的 3 个文件结尾也列一下:

src/base/file/deleteDir.ts
import shell from 'shelljs';export const deleteDir = async (path: string): Promise<boolean> => {  console.log(path)};
src/base/shell/closePort.ts
import shell from 'shelljs';export const closePort = async (port: string): Promise<boolean> => {  console.log(port);};
src/base/shell/gitCheckout.ts
import shell from 'shelljs';/** * @name 切换分支 * @description 指令合并: * 1. git checkout ${branch} * 2. git pull * @param {string} branch 分支名 */export const gitCheckout = (branch: string): boolean => {  console.log(branch)};
另外 common 下的 sortCatalog.ts 迁徙到 base/file 目录下,并且改名为 sortDir.ts 了。

那么,筹备结束,开始输入!

五 敞开端口

在起一些神奇的服务时,会碰到端口被占用的场景,这时候就须要敞开端口:

  • 查看端口占用状况:netstat -ano|findstr "端口号"
PS F:\xxx> netstat -ano|findstr "3001"  TCP    0.0.0.0:3001           0.0.0.0:0              LISTENING       33396  TCP    10.13.144.170:63001    183.2.199.241:443      ESTABLISHED     28228  TCP    [::]:3001              [::]:0                 LISTENING       33396
  • 终止 PID:taskkill -F -PID PID号
PS F:\xxx> taskkill -F -PID 33396胜利: 已终止 PID 为 33396 的过程。

那么到了 Node 工具库这边,必定就不要本人去这样操作啦,搞个省事的形式吧:

更新 src/common/index.ts

更新 src/common/questionList.ts

而后编写 closePort.ts

src/base/shell/closePort.ts
import shell from 'shelljs';export const closePort = async (port: string): Promise<boolean> => {  await shell.exec(`netstat -ano | findstr :${port}`);  // Windows 下会返回一个端口占用清单,须要自行删除  console.log('已找到下面清单列表,请执行指令删除端口:taskkill -F -PID PID号');   return await true;};
注:Windows 打印后果最开端的为 PID 号

当然,因为 3001 可能会有好几个 ip 对应的端口,所以前面那个步骤咱们仅做了提醒,而不是敞开了所有 3001 的端口(须要用户手动操作)。

然而这样总好过咱们去记忆这个指令(毕竟 Windows 和 Mac 等的操作指令还不通)

执行 npm run jsliang,运行后果如下:

这样咱们就封装好了敞开端口的,因为不是一键彻底敞开,实用指数给到 ☆☆☆

六 删除文件/文件夹

之前在公司为了钻研 Windows 如何疾速删除 node_modulesjsliang 还真翻了下材料找到 3 种删除文件/文件夹的形式:

  1. cmd.exerd /s /q <path>
  2. PowerShellrd -r <path>
  3. Macrm -rf <path>

通过屡次亲自体验,在公司中的 32G 内存,500 SSD 的台式电脑中,通过 PowerShell 的删除操作比 cmd.exe 的快(别问我为啥,反正就是快点,仅集体体验,不做迷信撑持)。

而后看了下 ShellJS,是有删除形式的:

  • ShellJS:rm() 删除文件,rm('rf', <path>) 删除文件夹

当然!奔着摸索精力,咱们看看它源码咋实现的:

  • GitHub:ShellJS - rm.js
function rmdirSyncRecursive(dir, force, fromSymlink) {    // 1. 先删除目录中的所有文件  let files = fs.readdirSync(dir);  for (var i = 0; i < files.length; i++) {    // 1.1 如果是目录则递归调用 rmdirSyncRecursive()    // 1.2 如果是文件则调用 fs.unlinkSync() 执行删除  }  // 2. 再删除目录  fs.rmdirSync();}

当然,外面有些细节还是写得不错的,这里就不开展具体解说,感兴趣的小伙伴能够点击下面链接摸索下源码。

所以,咱们就用 ShellJS 的办法吧!如果前面感觉不难受再替换为零碎指令。

更新 src/common/index.ts

更新 src/common/questionList.ts

而后开始编写 deleteDir.ts

src/base/file/deleteDir.ts
import shell from 'shelljs';export const deleteDir = async (path: string): Promise<boolean> => {  /**   * cmd.exe:rd /s /q <path>   * PowerShell:rd -r <path>   * Mac:rm -rf <path>   * ShellJS:rm() 删除文件,rm('rf', <path>) 删除文件夹   */  await shell.rm('-rf', path);  return true;};

执行 npm run jsliang,打印内容如下:

搞定,出工!因为不分明 Node 操作和零碎指令哪个比拟快,所以暂定实用指数 ☆☆☆

七 Git 操作

那么最初,来到重头 Git 操作。

想必有些小伙伴会跟 jsliang 一样懒?

  • git add .
  • git commit -m "xxx"
  • git push

曾经到了麻痹的状态了,jsliang 甚至开发了特定的 VS Code 插件:

注:VS Code 也卷,更新太快了我插件承受不了(肯定水平上最新 VS Code 用不了该插件),所以我的 VS Code 版本锁死 v1.53.2,啥时候有空操作再更新这个插件了

在 VS Code 插件中,进行疾速的提交操作。

所以在一些惯例的 Git 操作咱们还是心愿能封装起来(不须要记指令,也不想点页面,让它自行跑起来吧)

7.1 工作中罕用 Git 指令

jsliang 在工作中常常应用,并记住了的对应指令:

  • git pull:拉取代码并主动合并,jsliang 这边还会用 git pull --rebase origin master,表明拉取近程分支并基于该分支进行改变
  • git checkout -b <newBranch>:从以后分支切新分支
  • git branch -D <branch>:依据分支名删除指定分支
  • git add <files>:提交到暂存区
  • git commit <description>:提交到本地版本库。如果你们仓库有 eslint 查看之类的,强烈推荐 git commit -m "xxx" --no-verify(有时候真不想搞啥查看)
  • git push:提交到近程库。个别新分支操作为 git push -- set upstream origin <branch>
  • git cherry-pick <commitHash>:将指定的提交(commit)利用于其余分支
  • git stash:暂存内容。将暂存区的内容存储到栈中(屡次 stash 能够通过屡次 pop 推出来)
  • git stash pop:签出内容。将 git stash 中的内容推出来
  • git reset --soft HEAD^:回退版本并保留内容。这个 HEAD^ 是指上一个版本,也能够写成 HEAD~1(就是 commit id)

值得一提的是,jsliang 之前还尝试用过:git worktree,它能够同时批改多个版本。

然而因为嫌麻烦(要记指令),所以就没用了

同一个 Git 仓库,须要同时批改多个分支,或者须要在 A 分支上参照 B 分支的内容进行批改。

当然这种状况能够用 git clone 拷贝一个新仓库,然而如果你的仓库有点大(几 G 那种),那还是有点嫌麻烦的。

于是就有了 git worktree,指令如下:

# 将 abc 分支切出到另一个目录 jsliang 中# 留神:这个目录不能在主仓库中git worktree add ../jsliang abc # git add [<选项>] <门路> [<提交>]# 获取帮忙git worktree -h# 查看每个工作树的详细信息git worktree list# 更残缺的工作树信息# git worktree list --porcelain# 锁定内容,避免被主动删除git worktree lock# 解锁内容git worktree unlock# 迁徙到新目录git worktree move abc ../jsliang2# 删除某条工作树git worktree remove ../jsliang# 革除工作树信息git worktree prune

罕用 git worktree 指令:

  • 切出分支:git worktree add ../jsliang abc
  • 罕用操作:git add .git commit -m "xxx"git push
  • 敞开分支:git worktree prune

当然,还有 Git 设置代理

迷信上网状况下,有时候 Git 并没有失效,克隆或者 push 操作一样卡慢,就须要设置 Git 代理。

  • 设置代理
  1. git config --global http.proxy 代理地址
  2. git config --global https.proxy 代理地址
  • 勾销代理
  1. git config --global --unset http.proxy
  2. git config --global --unset https.proxy
  • 查看曾经设置的代理
  1. git config --global --get http.proxy
  2. git config --global --get https.proxy

我拿当初用的迷信上网代理软件,就设置了 git config --global http.proxy http://127.0.0.1:10809,Git 晦涩度晋升了挺多。

7.2 切换分支

吧啦吧啦说了一通,上面开始干点正活,咱们搞个简略的切换分支:

更新 src/common/index.ts

更新 src/common/questionList.ts

接着更新下 gitCheckout.ts

src/base/shell/gitCheckout.ts
import shell from 'shelljs';/** * @name 切换分支 * @description 指令合并: * 1. git checkout ${branch} * 2. git pull * @param {string} branch 分支名 */export const gitCheckout = (branch: string): boolean => {  if (!shell.which('git')) {    shell.echo('Error: 请先装置 Git!');    shell.exit(1);  }  console.log('开始切换分支:');  const checkoutResult = shell.exec(`git checkout ${branch}`);  if (checkoutResult.code !== 0) {    shell.echo('Error: 切分支失败!');    shell.exit(1);  }  console.log('开始拉取代码,请稍等:');  const pullResult = shell.exec('git pull');  const { stdout, stderr } = pullResult;  if (pullResult.code === 0) {    if (stdout.includes('from the remote, but no such ref was fetched.')) {      console.log('你的分支是最新的');    }   } else if (pullResult.code === 1) {    if (stderr.includes('There is no tracking information for the current branch.')) {      console.log('不存在近程分支');    }  }  return true;};

而后运行:npm run jsliang,跟着指令输出分支名:

此时因为是咱们轻易输出的一个分支,所以它表明切换分支失败了。

当然,小伙伴们能够根据实在业务往里面装载更多的内容,这里就不开展具体解说,仅做抛砖引玉先吧~

八 总结

其实最近两天还有在通过汇总 bash 指令来压缩一些工作流程的事,然而碍于一时半会想不到更好的我的项目例子,所以就没有一一铺开解说,所以小伙伴们碰到这种状况,欢送来吐槽评论留言,衷心示意心愿能和小伙伴们一起探讨。

那么,对于 ShellJS 咱们就铺垫那么一点了,前面 jsliang 在生存、工作中碰到一些乏味的知识点再增加进来吧~

So,下期再会!

九 参考文献

  • GitHub:ShellJS - Unix shell commands for Node.js
  • 掘金: nodejs写bash脚本终极计划!
  • GitHub:Git worktree 作用及应用
  • 简书:git worktree 的应用
  • 知乎:Git屠龙技:应用Git Worktree并行开发测试
  • 政企云前端团队:我在工作中是如何应用 Git 的
  • SegmentFault:Git 屠龙技:应用 Git Worktree 并行开发测试
  • 何方的编程之路:Git如何应用代理(VPN)
  • 阮一峰:cherry-pick

jsliang 的文档库由 梁峻荣 采纳 常识共享 署名-非商业性应用-雷同形式共享 4.0 国内 许可协定 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协定受权之外的应用权限能够从 [https://creativecommons.org/l...](https://creativecommons.org/l...