Node Series-008-ShellJS

jsliang
中文

A catalog

The front end that is not tossing, what is the difference with the salted fish

table of Contents
A catalog
Two preface
Three Node solutions for writing bash scripts
Four programming front
Five closed ports
Six delete files/folders
Seven Git operations
7.1 Commonly used Git commands in work
7.2 Switch branches
Eight summary
Nine references

Two preface

Today jsliang 160f8ca2b04893 took a at work and opened a VIP channel:

  1. Download Excel files automatically
  2. Copy to the specified directory
  3. Perform multilingual import operations
  4. git push imported resource 060f8ca2b04926

Although the rough operation looks like this, there is nothing to say.

But in operation, it may be more cumbersome. For example, after importing resources, the git operation is performed:

  1. Add staging area: git add .
  2. Switch branch: git checkout -b <branch>
  3. Submit the local repository: git commit -m "feat: "multilingual" new resource #master_0720"
  4. Submit remote branch: git push --set-upstream origin <branch>

Of course, it's not just this, you must check whether the build can pass smoothly after adding your multilingual resources before submitting...


bash about it, in fact, it is purely to elicit the operation of the 060f8ca2b04a52 instruction.

At work, you may encounter:

  • Git series operations
  • Close the occupied system port
  • Delete specified files/folders, etc.
  • ……

However, for these operations, you may forget its instructions for a while, or its instructions are too cumbersome, so jsliang feels that it is not easy to install these contents?

Three Node solutions for writing bash scripts

In fact, regarding this solution, jsliang is still too troublesome, so I went directly to ShellJS :

If you feel that it is a bit abrupt to directly go to the plan like this, you can see:

The author compared Node's own child_process API, ShellJS and zx , and finally adopted the zx plan.

Of course, jsliang work. ShellJS don’t want to be so tired to explore similar libraries, so install ShellJS ~

  • Installation: npm i shelljs
  • Install TS to compile: npm i @types/shelljs -D

After the installation is complete, start tossing!

Four programming front

In order to make the code not look so stinky and long.

jsliang finishing writing the article, I read the whole article more than 3 times, and the repetitive code and problem situation in the following are organized here, so before you copy the code, please read this chapter carefully, please ~

warn! please read the following carefully

The full-text catalog is as follows, please remember to create a new catalog in advance:

shell-00.png

Then repeat the code as follows:

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 工作',
    }
  },
};

The following article shows:

Update src/common/index.ts

Update src/common/questionList.ts

In this case, you don't need to operate it. There are 3 places in this article that have this description.

Similarly, in order to avoid reporting TypeScript errors when quoting and running the code, the three newly added files are also listed at the beginning:

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)
};
In addition, common under sortCatalog.ts migrated to the base/file directory and renamed sortDir.ts .

So, when you are ready, start outputting!

Five closed ports

When starting some magical services, you will encounter scenarios where the port is occupied. At this time, you need to close the port:

  • View port occupancy: netstat -ano|findstr "port number"
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
  • Terminate PID: taskkill -F -PID PID number
PS F:\xxx> taskkill -F -PID 33396
成功: 已终止 PID 为 33396 的进程。

So when you get to the Node tool library, you definitely don't want to do this by yourself, let's find a way to save trouble:

Update src/common/index.ts

Update src/common/questionList.ts

Then write 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;
};
Note: The PID number at the end of the Windows print result

Of course, because 3001 may have several ip corresponding to 060f8ca2b05130, we only made a prompt in the next step, instead of closing all 3001 ports (users need to operate manually).

But this is better than memorizing this instruction (after all, operating instructions such as Windows and Mac are still unavailable)

Execute npm run jsliang and the results are as follows:

shell-01.png

In this way, we have encapsulated the port closure, because it is not completely closed with one key, the practical index is given to ☆☆☆

Six delete files/folders

In order to study Windows how to quickly delete node_modules , jsliang in the company before, I really checked the information and found 3 ways to delete files/folders:

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

After much experience in the company of 32G of memory, 500 SSD desktop computer, by PowerShell deletion than cmd.exe fast (do not ask me why, anyway, is quick, only personal experience, not scientific support) .

Then I took a look at ShellJS , there is a way to delete it:

  • ShellJS: rm() deletes files, rm('rf', <path>) deletes folders

of course! In the spirit of exploration, let's take a look at how its source code is implemented:

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();
}

Of course, some of the details are well written, so I won’t explain them in detail here. Interested friends can click on the link above to explore the source code.

So, let's use the method of ShellJS If you feel uncomfortable later, replace it with system commands.

Update src/common/index.ts

Update src/common/questionList.ts

Then start writing 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;
};

Execute npm run jsliang , the print content is as follows:

shell-02.png

Get it done, call it a day! Because it’s not clear which Node operation or system command is faster, so tentatively set a practical index☆☆☆

Seven Git operations

So finally, come to the re-start of Git operations.

Presumably some friends will be as lazy as jsliang

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

Has reached a numb state, jsliang even developed a specific VS Code plug-in:

shell-03.png

Note: VS Code is also volume, the update is too fast and I cannot accept the plug-in (to a certain extent, the latest VS Code cannot use the plug-in), so my VS Code version is locked v1.53.2 , and I will update this plug-in when I have free operation

In the VS Code plug-in, perform quick submission operations.

So we still hope to encapsulate some regular Git operations (you don't need to remember instructions, and don't want to click on the page, let it run by itself)

7.1 Commonly used Git commands in work

jsliang is often used in work and remembered the corresponding instructions:

  • git pull : Pull the code and merge it automatically, jsliang will also use git pull --rebase origin master here, indicating that the remote branch is pulled and changes are made based on the branch
  • git checkout -b <newBranch> : Cut a new branch from the current branch
  • git branch -D <branch> : Delete the specified branch according to the branch name
  • git add <files> : Submit to the staging area
  • git commit <description> : Submit to the local repository. If your warehouse has eslint inspections and the like, git commit -m "xxx" --no-verify strongly recommended (sometimes I don’t want to do any inspections)
  • git push : Submit to the remote library. The general new branch operation is git push -- set upstream origin <branch>
  • git cherry-pick <commitHash> : Apply the specified commit ( commit ) to other branches
  • git stash : Temporarily store content. Store the contents of the temporary storage area in the stack (multiple times stash can be launched by multiple times pop
  • git stash pop : Check out content. git stash out the content in 060f8ca2b0579a
  • git reset --soft HEAD^ : Roll back the version and keep the content. This HEAD^ refers to the previous version, and can also be written as HEAD~1 (that is, the commit id)

It is worth mentioning that jsliang also tried before: git worktree , it can modify multiple versions at the same time.

But because it is too troublesome (remember instructions), it is useless

In the same Git repository, multiple branches need to be modified at the same time, or the A branch needs to be modified with reference to the content of the B branch.

Of course, in this case, you can use git clone copy a new warehouse, but if your warehouse is a bit large (a few gigabytes), it is still a bit troublesome.

So there is git worktree , the instruction is as follows:

# 将 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

Commonly used git worktree instructions:

  • Cut out the branch: git worktree add ../jsliang abc
  • Common operations: git add . , git commit -m "xxx" , git push
  • Close branch: git worktree prune

Of course, there is also Git to set up the proxy

In the case of scientific Internet access, sometimes Git does not take effect, and cloning or push is as slow as the operation, so you need to set up a Git proxy.

  • Set up proxy
  1. git config --global http.proxy proxy address
  2. git config --global https.proxy proxy address
  • Cancel proxy
  1. git config --global --unset http.proxy
  2. git config --global --unset https.proxy
  • View the proxy that has been set
  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 with my current scientific Internet proxy software, and the smoothness of Git has improved a lot.

7.2 Switch branches

Let's talk about it, let's do some real work, let's make a simple switch branch:

Update src/common/index.ts

Update src/common/questionList.ts

Then update 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;
};

Then run: npm run jsliang , follow the instructions to enter the branch name:

shell-04.png

At this time, because it is a branch that we randomly input, it indicates that the switch branch failed.

Of course, friends can load more content into it based on the real business, so I won’t explain it in detail here, just do a good job first~

Eight summary

In fact, in the last two days, we have bash instructions, but since we will not think of a better project example for a while, we did not explain them one by one, so my friends encountered this situation. , Welcome to comment and leave a message, sincerely hope to discuss with friends.

So, let’s pave the way ShellJS jsliang will add some interesting knowledge points in life and work.

So, see you next time!

Nine references


jsliang's document library is by 160f8ca2b060df Liang Junrong using Creative Commons Attribution-Non-commercial Use-Same Method Sharing 4.0 International License Agreement . <br/>Based on the works https://github.com/LiangJunrong/document-library <br/>The use rights not authorized by this license agreement can be obtained from [ https://creativecommons.org/licenses/by-nc-sa/2.5/cn/](https://creativecommons.org/licenses/by -nc-sa/2.5 shell-00.png
阅读 326

前端工程师

285 声望
15 粉丝
0 条评论
你知道吗?

前端工程师

285 声望
15 粉丝
宣传栏