Tiven

Tiven

博观而约取,厚积而薄发

天问的个人网站(天问博客),专注于Node.js、Vue.js、React、Vite、Npm、Nginx等大前端技术。不断学习新技术,记录日常开发问题,持续分享coding,极客开源,共同进步。生命不息,奋斗不止... [ hexo blog ]

使用 nodejs + zx 编写脚本更新本地 hosts


在日常的开发和运维工作中,我们经常需要修改本地的 hosts 文件来实现一些特定的网络配置,比如将某个域名指向特定的 IP 地址。而使用 Node.js 结合 zx 工具可以让我们更轻松地编写和运行脚本来更新本地的 hosts 文件。

nodejs zx shell

一、功能介绍

目标: 通过修改 Hosts 解决国内 Github 经常抽风访问不到的情况

  1. 获取 github 上的远程 hosts 文件内容,更新本地 hosts 文件
  2. 恢复系统原有 hosts 文件
  3. 刷新 DNS 缓存,使配置生效

二、准备工作

首先,确保你已经安装了 Node.jsnpm。然后,通过 npm 安装 zx 工具:

npm install -g zx

三、编写脚本

接下来,我们来编写一个简单的 Node.js 脚本,使用 zx 来更新本地的 hosts 文件。以下是一个示例脚本,可以根据实际需求进行修改:

  1. 更新 hosts 脚本 update-hosts.mjs
#!/usr/bin/env zx

import { $, spinner } from 'zx'
import axios from 'axios'
import chalk from 'chalk'
import { dir, hostsPath, sh, startStr, endStr } from './config/hosts.config.mjs'

// 远程获取 hosts 文件内容,
async function getHostsContent() {
  try {
    const response = await axios({
      url: `https://api.github.com/repos/maxiaof/github-hosts/contents/hosts`,
    })
    console.log(response.data.content)
    // await $`echo ${response.data.content} > ${dir}/base64.txt`
    const readmeContent = Buffer.from(response.data.content, 'base64').toString(
      'utf-8'
    )
    return readmeContent
  } catch (error) {
    console.error('Error fetching README:', error.message)
  }
}

async function saveReadmeFile() {
  try {
    const response = await axios({
      url: `https://api.github.com/repos/maxiaof/github-hosts/contents/README.md`,
    })
    const readmeContent = Buffer.from(response.data.content, 'base64').toString(
      'utf-8'
    )
    await $`echo ${readmeContent} > ${dir}/README.md`
  } catch (e) {
    console.log(e)
  }
}

async function readLine(str = '') {
  let hosts = ''
  let isHosts = false
  str.split('\n').forEach(line => {
    if (line.includes(startStr)) {
      isHosts = true
    }
    if (isHosts) {
      hosts += `${line}\n`
    }
    if (line.includes(endStr)) {
      isHosts = false
    }
  })

  return hosts
}

async function runShell(hosts) {
  try {
    console.log(hosts)
    // await $`pwd`
    // 写入 github hosts
    await $`echo ${hosts} > ./host/github.hosts`
    // 复制系统原有 hosts 到新 hosts
    await $`cp -f ./host/base.hosts ./host/hosts`
    // 在新 hosts 后追加 github hosts
    await $`cat ./host/github.hosts >> ./host/hosts`
    // 复制新 hosts 到 /etc/hosts
    await $`cp -f ./host/hosts ${hostsPath}`
    // 刷新DNS,激活生效
    await $`echo ${sh}`
  } catch (p) {
    console.log(`Exit code: ${p.exitCode}`)
    console.log(`Error: ${p.stderr}`)
    await $`exit 1`
  }
}

async function initHostsFile() {
  try {
    let error = (await $`ls ${dir}`).stderr
    if (!error) {
      console.log(`${dir} 文件夹已存在`)
    }
  } catch (err) {
    console.log('文件夹不存在')
    await $`mkdir ${dir}`
    // 下载保存 README.md
    await saveReadmeFile()
    // 备份系统 hosts
    await $`sudo cp ${hostsPath} ${dir}/os.hosts`
    // 第一次初始化,把系统 hosts 作为 base.hosts
    await $`sudo cp ${hostsPath} ${dir}/base.hosts`
    await $`touch ${dir}/github.hosts`
    await $`touch ${dir}/hosts`
  }
}

!(async function run() {
  // 初始化目录文件
  await initHostsFile()
  await spinner('running...', async () => {
    let hosts = await getHostsContent()
    await runShell(hosts)
  })
  await spinner('running...', () => $`sleep 2`)
  console.log(chalk.green('Local hosts updated successfully!'))
})()
  1. 恢复系统 hosts 脚本 reset-hosts.mjs
#!/usr/bin/env zx

// // eslint-disable-next-line
// /* eslint-disable */
import { $, spinner } from 'zx' // eslint-disable-line
import chalk from 'chalk'
import { dir, hostsPath, sh } from './config/hosts.config.mjs'

async function runShell() {
  try {
    await $`ls ${dir}`
    // 复制基础 hosts 到新 hosts
    await $`cp -f ./host/base.hosts ./host/hosts`
    // 复制新 hosts 到 /etc/hosts
    await $`cp -f ./host/hosts ${hostsPath}`
    // 刷新DNS,激活生效
    await $`echo ${sh}`
  } catch (p) {
    console.log(`Exit code: ${p.exitCode}`)
    console.log(`Error: ${p.stderr}`)
    await $`exit 1`
  }
}

!(async function run() {
  await spinner('running...', async () => {
    await runShell()
  })
  await spinner('running...', () => $`sleep 2`)
  console.log(chalk.green('Local hosts reset successfully!'))
})()
  1. 配置文件 hosts.config.mjs
export const osMap = {
  darwin: {
    name: 'MacOS',
    hostsPath: '/etc/hosts',
    sh: 'sudo killall -HUP mDNSResponder',
  },
  linux: {
    name: 'Linux',
    hostsPath: '/etc/hosts',
    sh: 'sudo nscd restart',
  },
  win32: {
    name: 'Windows',
    hostsPath: 'C:/Windows/System32/drivers/etc/hosts',
    sh: 'ipconfig /flushdns',
  },
}

export const dir = './host'

export const { platform } = process

export const { hostsPath, sh } = osMap[platform]

export const startStr = '#Github Hosts Start'

export const endStr = '#Github Hosts End'

四、运行脚本

运行脚本有两种方式:

  1. update-hosts.mjsreset-hosts.mjs 脚本作为可执行文件,前提是全局安装了 zx 工具
  2. 使用 node 命令运行脚本,需要本地安装 axioschalkzx 等依赖

方式一:可执行文件执行

赋予执行权限:

chmod +x update-hosts.mjs
chmod +x reset-hosts.mjs

然后在终端中运行该脚本:

./updateHosts.js
# 或
./resetHosts.js

方式二:node 命令执行

node update-hosts.mjs
node reset-hosts.mjs
# sudo 权限执行(MacOS)
sudo node update-hosts.mjs
sudo node reset-hosts.mjs

其中涉及 sudo 权限(MacOS),需要输入密码。 windows 系统下,需要管理员权限运行,或以管理员身份运行终端 cmd。

参考文档:

  • https://google.github.io/zx/
  • https://www.npmjs.com/package/zx

欢迎访问:天问博客