如何用脚本编辑文件(aep格式用什么打开编辑)?如果你对这个不了解,来看看!
社区精选|如何使用 zx 编写 shell 脚本,下面是计算科学与信息化给大家的分享,一起来看看。
如何用脚本编辑文件
前言
在这篇文章中,我们将学习谷歌的 zx 库提供了什么,以及我们如何使用它来用 Node.js 编写 shell 脚本。然后,我们将学习如何通过构建一个命令行工具来使用 zx 的功能,帮助我们为新的 Node.js 项目引导配置。
编写 Shell 脚本的问题
创建一个由 Bash 或者 zsh 执行的 shell 脚本,是自动化重复任务的好方法。Node.js 似乎是编写 shell 脚本的理想选择,因为它为我们提供了许多核心模块,并允许我们导入任何我们选择的库。它还允许我们访问 JavaScript 提供的语言特性和内置函数。
如果你尝试编写运行在 Node.js 中的 shell 脚本,你会发现这没有你想象中的那么顺利。你需要为子进程编写特殊的处理程序,注意转义命令行参数,然后最终与 stdout(标准输出)和 stderr(标准错误)打交道。这不是特别直观,而且会使 shell 脚本变得相当笨拙。
Bash shell 脚本语言是编写 shell 脚本的普遍选择。不需要编写代码来处理子进程,而且它有内置的语言特性来处理 stdout 和 stderr。但是用 Bash 编写 shell 脚本也不是那么容易。语法可能相当混乱,使得它实现逻辑,或者处理诸如提示用户输入的事情非常困难。
谷歌的 zx 库有助于让使用 Node.js 编写的 shell 脚本变得高效和舒适。
前置条件
往下阅读之前,有几个前置条件需要遵循:
理想情况下,你应该熟悉 JavaScript 和 Node.js 的基础知识。你需要适应在终端中运行命令。你需要安装 Node.js >= v14.13.1。本文中的所有代码都可以从 GitHub https://link.segmentfault.com/?enc=ysCUhsc%2BhqUmtqCo55t8jw%3D%3D.aWhjUaPje6eTlkcFFdhW%2FeIVYyAz5G%2FoPbGuXjsxlpcJphMKguwz3NoHWQ9o2vDb47Nfnm9kpIP6Ol5r6Euc8A%3D%3D 上获得。
zx 如何运作
Google 的 zx 提供了创建子进程的函数,以及处理这些进程的 stdout 和 stderr 的函数。我们将使用的主要函数是$函数。下面是它的一个实际例子:
import{nbsp;}from"zx";await社区精选|如何使用 zx 编写 shell 脚本-今日头条ls`;下面是执行上述代码的输出:
nbsp;lsbootstrap-toolhello-worldnode_modulespackage.jsonREADME.mdtypescript上面的例子中的 JavaScript 语法可能看起来有点古怪。它使用了一种叫做带标签的模板字符串 https://link.segmentfault.com/?enc=VUkodq5er%2Fynbhfl3MUQZA%3D%3D.%2Flx6oaDCVK4XuYyYLqvDi2QMWjCwW1jKBvNQgfaGG0AVwpl7I2CYD4sJYHuonDSA6jj1qSSypc0aGVO%2BYuBMUiibG6pBkVwusg%2Bai1hbMXetqlwMTWVUEAbtabMCbXIs 的语言特性。它在功能上与编写 await $("ls")相同。
谷歌的 zx 提供了其他几个实用功能,使编写 shell 脚本更容易。比如:
cd()。允许我们更改当前工作目录。question()。这是 Node.js readline 模块的包装器。它使提示用户输入变得简单明了。除了 zx 提供的实用功能外,它还为我们提供了几个流行的库,比如:
chalk。https://link.segmentfault.com/?enc=%2FL15Y8OQNrp05Scx6N4iaQ%3D%3D.r8AZkfcE1Ye%2BlNUznFA9RNJMhfyM0lttTiD0TmqjJQwoi7zjQjs5YiLjI%2FWDEooY 这个库允许我们为脚本的输出添加颜色。
minimist。https://link.segmentfault.com/?enc=5tMQ5d6qJ4STEHZeuiN0MA%3D%3D.x9GPOpXVZp5TGKknZCkwA10QP%2Ftw%2BW7fwnpYGg%2BnlJPJLK3RboT3jqy5WuPLFwPZ 一个解析命令行参数的库。然后它们在 argv 对象下被暴露出来。
fetch。https://link.segmentfault.com/?enc=71LC44tCu%2FZoLOY2B7MfuA%3D%3D.GO%2FMi37T0KTmj8UXau9xdFUBiIk1I%2F8M%2FHk2hZYrhRb%2Fd3Vl0bzlr05hanNxzuVb Fetch API 的 Node.js 实现。我们可以用它来进行 HTTP 请求。
fs-extra。https://link.segmentfault.com/?enc=cPZzXniTRFJdbq87FdwLWw%3D%3D.IsmhSUEjIcZAqw8FDidbUnNhhIVPT1gsFSo%2BchiXCAs9AaxUVT%2FEgbIvswUDVmK2 一个暴露 Node.js 核心 fs 模块的库,以及一些额外的方法,使其更容易与文件系统一起工作。
现在我们知道了 zx 给了我们什么,让我们用它创建第一个 shell 脚本。
zx 如何使用
首先,我们先创建一个新项目:
mkdirzx-shell-scriptscdzx-shell-scriptsnpminit--yes然后安装 zx 库:
npminstall--save-devzx注意:zx 的文档建议用 npm 全局安装该库。通过将其安装为我们项目的本地依赖,我们可以确保 zx 总是被安装,并控制 shell 脚本使用的版本。
顶级 await
为了在 Node.js 中使用顶级 await,也就是 await 位于 async 函数的外部,我们需要在 ES 模块的模式下编写代码,该模式支持顶级 await。
我们可以通过在 package.json 中添加 "type": "module" 怎么怎么来表明项目中的所有模块都是 ES 模块。或者我们可以将单个脚本的文件扩展名设置为 .mjs 。在本文的例子中,我们将使用 .mjs 文件扩展名。
运行命令并捕获输出
创建一个新脚本,将其命名为 hello-world.mjs 。我们将添加一个 Shebang 行,它告诉操作系统(OS)的内核要用 node 程序运行该脚本:
#!/usr/bin/envnode然后,我们添加一些代码,使用 zx 来运行命令。
在下面的代码中,我们运行命令执行 ls 程序。ls 程序将列出当前工作目录(脚本所在的目录)中的文件。我们将从命令的进程中捕获标准输出,将其存储在一个变量中,然后打印到终端:
//hello-world.mjsimport{nbsp;}from"zx";constoutput=(await社区精选|如何使用 zx 编写 shell 脚本-今日头条ls`).stdout;console.log(output);注意:zx 文档建议把/usr/bin/env zx 放在我们脚本的 shebang 行中,但我们用/usr/bin/env node 代替。这是因为我们已经安装 zx,并作为项目的本地依赖。然后我们明确地从 zx 包中导入我们想要使用的函数和对象。这有助于明确我们脚本中使用的依赖来自哪里。
我们使用 chmod 来让脚本可执行:
chmodu+xhello-world.mjs运行项目:
./hello-world.mjs可以看到如下输出:
nbsp;lshello-world.mjsnode_modulespackage.jsonpackage-lock.jsonREADME.mdhello-world.mjsnode_modulespackage.jsonpackage-lock.jsonREADME.md你会注意到:
我们运行的命令(ls)被包含在输出中。命令的输出显示两次。在输出的末尾多了一个新行。zx 默认以 verbose 模式运行。它将输出你传递给$函数的命令,同时也输出该命令的标准输出。我们可以通过在运行 ls 命令前加入以下一行代码来改变这种行为:
$.verbose=false;大多数命令行程序,如 ls,会在其输出的结尾处输出一个新行字符,以使输出在终端中更易读。这对可读性有好处,但由于我们要将输出存储在一个变量中,我们不希望有这个额外的新行。我们可以用 JavaScript String#trim()函数把它去掉:
-constoutput=(await社区精选|如何使用 zx 编写 shell 脚本-今日头条ls`).stdout;+constoutput=(await社区精选|如何使用 zx 编写 shell 脚本-今日头条ls`).stdout.trim();再次运行脚本,结果看起来好很多:
hello-world.mjsnode_modulespackage.jsonpackage-lock.json引入 TypeScript
如果我们想在 TypeScript 中编写使用 zx 的 shell 脚本,有几个微小的区别我们需要加以说明。
注意:TypeScript 编译器提供了大量的配置选项,允许我们调整它如何编译我们的 TypeScript 代码。考虑到这一点,下面的 TypeScript 配置和代码是为了在大多数 TypeScript 版本下工作。
首先,安装需要运行 TypeScript 代码的依赖:
npminstall--save-devtypescriptts-nodets-node 包提供了一个 TypeScript 执行引擎,让我们能够转译和运行 TypeScript 代码。
需要创建 tsconfig.json 文件包含下面的配置:
{"compilerOptions":{"target":"es2017","module":"commonjs"}}创建新的脚本,并命名为 hello-world-typescript.ts。首先,添加 Shebang 行,告诉 OS 内核使用 ts-node 程序来运行我们的脚本:
#!./node_modules/.bin/ts-node为了在我们的 TypeScript 代码中使用 await 关键字,我们需要把它包装在一个立即调用函数表达式(IIFE)中,正如 zx 文档所建议的那样:
//hello-world-typescript.tsimport{nbsp;}from"zx";void(asyncfunction(){await社区精选|如何使用 zx 编写 shell 脚本-今日头条ls`;})();然后需要让脚本可执行:
chmodu+xhello-world-typescript.ts运行脚本:
./hello-world-typescript.ts可以看到下面的输出:
nbsp;lshello-world-typescript.tsnode_modulespackage.jsonpackage-lock.jsonREADME.mdtsconfig.json在 TypeScript 中用 zx 编写脚本与使用 JavaScript 相似,但需要对我们的代码进行一些额外的配置和包装。
构建项目启动工具
现在我们已经学会了用谷歌的 zx 编写 shell 脚本的基本知识,我们要用它来构建一个工具。这个工具将自动创建一个通常很耗时的过程:为一个新的 Node.js 项目的配置提供引导。
我们将创建一个交互式 shell 脚本,提示用户输入。它还将使用 zx 内置的 chalk 库,以不同的颜色高亮输出,并提供一个友好的用户体验。我们的 shell 脚本还将安装新项目所需的 npm 包,所以它已经准备好让我们立即开始开发。
准备开始
首先创建一个名为 bootstrap-tool.mjs 的新文件,并添加 shebang 行。我们还将从 zx 包中导入我们要使用的函数和模块,以及 Node.js 核心 path 模块:
#!/usr/bin/envnode//bootstrap-tool.mjsimport{$,argv,cd,chalk,fs,question}from"zx";importpathfrom"path";与我们之前创建的脚本一样,我们要使我们的新脚本可执行:
chmodu+xbootstrap-tool.mjs我们还将定义一个辅助函数,用红色文本输出一个错误信息,并以错误退出代码 1 退出 Node.js 进程:
functionexitWithError(errorMessage){console.error(chalk.red(errorMessage));process.exit(1);}当我们需要处理一个错误时,我们将通过我们的 shell 脚本在各个地方使用这个辅助函数。
检查依赖
我们要创建的工具需要使用三个不同程序来运行命令:git、node 和 npx。我们可以使用 which 库来帮助我们检查这些程序是否已经安装并可以使用。
首先,我们需要安装 which:
npminstall--save-devwhich然后引入它:
importwhichfrom"which";然后创建一个使用它的 checkRequiredProgramsExist 函数:
asyncfunctioncheckRequiredProgramsExist(programs){try{for(letprogramofprograms){awaitwhich(program);}}catch(error){exitWithError(`Error:Requiredcommand${error.message}`);}}上面的函数接受一个程序名称的数组。它循环遍历数组,对每个程序调用 which 函数。如果 which 找到了程序的路径,它将返回该程序。否则,如果该程序找不到,它将抛出一个错误。如果有任何程序找不到,我们就调用 exitWithError 辅助函数来显示一个错误信息并停止运行脚本。
我们现在可以添加一个对 checkRequiredProgramsExist 的调用,以检查我们的工具所依赖的程序是否可用:
awaitcheckRequiredProgramsExist(["git","node","npx"]);添加目标目录选项
由于我们正在构建的工具将帮助我们启动新的 Node.js 项目,因此我们希望在项目的目录中运行我们添加的任何命令。我们现在要给脚本添加一个 --directory 命令行参数。
zx 内置了 minimist 包,它能够解析传递给脚本的任何命令行参数。这些被解析的命令行参数被 zx 包作为 argv 提供:
让我们为名为 directory 的命令行参数添加一个检查:
lettargetDirectory=argv.directory;if(!targetDirectory){exitWithError("Error:Youmustspecifythe--directoryargument");}如果 directory 参数被传递给了我们的脚本,我们要检查它是否是已经存在的目录的路径。我们将使用 fs-extra 提供的 fs.pathExists 方法:
targetDirectory=path.resolve(targetDirectory);if(!(awaitfs.pathExists(targetDirectory))){exitWithError(`Error:Targetdirectory'${targetDirectory}'doesnotexist`);}如果目标路径存在,我们将使用 zx 提供的 cd 函数来切换当前的工作目录:
cd(targetDirectory);如果我们现在在没有--directory 参数的情况下运行脚本,我们应该会收到一个错误:
nbsp;./bootstrap-tool.mjsError:Youmustspecifythe--directoryargument检查全局 Git 设置
稍后,我们将在项目目录下初始化一个新的 Git 仓库,但首先我们要检查 Git 是否有它需要的配置。我们要确保提交会被 GitHub 等代码托管服务正确归类。
为了做到这一点,这里创建一个 getGlobalGitSettingValue 函数。它将运行 git config 命令来检索 Git 配置设置的值:
asyncfunctiongetGlobalGitSettingValue(settingName){$.verbose=false;letsettingValue="";try{settingValue=(await社区精选|如何使用 zx 编写 shell 脚本-今日头条gitconfig--global--get${settingName}`).stdout.trim();}catch(error){//Ignoreprocessoutput}$.verbose=true;returnsettingValue;}你会注意到,我们正在关闭 zx 默认设置的 verbose 模式。这意味着,当我们运行 git config 命令时,该命令和它发送到标准输出的任何内容都不会被显示。我们在函数的结尾处将 verbose 模式重新打开,这样我们就不会影响到我们稍后在脚本中添加的任何其他命令。
现在我们添加 checkGlobalGitSettings 函数,该函数接收 Git 设置名称组成的数组。它将循环遍历每个设置名称,并将其传递给 getGlobalGitSettingValue 函数以检索其值。如果设置没有值,将显示警告信息:
asyncfunctioncheckGlobalGitSettings(settingsToCheck){for(letsettingNameofsettingsToCheck){constsettingValue=awaitgetGlobalGitSettingValue(settingName);if(!settingValue){console.warn(chalk.yellow(`Warning:Globalgitsetting'${settingName}'isnotset.`));}}}让我们给 checkGlobalGitSettings 添加一个调用,检查 user.name 和 user.email 的 Git 设置是否已经被设置:
awaitcheckGlobalGitSettings(["user.name","user.email"]);初始化 Git 仓库
我们可以通过添加以下命令在项目目录下初始化一个新的 Git 仓库:
await社区精选|如何使用 zx 编写 shell 脚本-今日头条gitinit`;生成 package.json
每个 Node.js 项目都需要 package.json 文件。这是我们为项目定义元数据的地方,指定项目所依赖的包,以及添加实用的脚本。
在我们为项目生成 package.json 文件之前,我们要创建几个辅助函数。第一个是 readPackageJson 函数,它将从项目目录中读取 package.json 文件:
asyncfunctionreadPackageJson(directory){constpackageJsonFilepath=`${directory}/package.json`;returnawaitfs.readJSON(packageJsonFilepath);}然后我们将创建一个 writePackageJson 函数,我们可以用它来向项目的 package.json 文件写入更改:
asyncfunctionwritePackageJson(directory,contents){constpackageJsonFilepath=`${directory}/package.json`;awaitfs.writeJSON(packageJsonFilepath,contents,{spaces:2});}我们在上面的函数中使用的 fs.readJSON 和 fs.writeJSON 方法是由 fs-extra 库提供的。
在定义了 package.json 辅助函数后,我们可以开始考虑 package.json 文件的内容。
Node.js 支持两种模块类型:
CommonJS Modules (CJS)。https://link.segmentfault.com/?enc=KTweLvts6a1zOlUbn0cnCA%3D%3D.WRSoJ3t37Y2GYjanjStt81N%2BkCOGoR11gK8pv6Fck09uaKTSXHq5mvMcWutIRxYU 使用 module.exports 来导出函数和对象,在另一个模块中使用 require()加载它们。ECMAScript Modules (ESM)。https://link.segmentfault.com/?enc=OUbF%2BkjeOpAlt3Fo9PFGFw%3D%3D.Ex%2BQVnop7rVNKtYxrnp%2Fhcm08eFRE7K2XLUBlFFQ7D4%3D 使用 export 来导出函数和对象,在另一个模块中使用 import 加载它们。Node.js 生态系统正在逐步采用 ES 模块,这在客户端 JavaScript 中是很常见的。当事情处于过渡阶段时,我们需要决定我们的 Node.js 项目默认使用 CJS 模块还是 ESM 模块。让我们创建一个 promptForModuleSystem 函数,询问这个新项目应该使用哪种模块类型:
asyncfunctionpromptForModuleSystem(moduleSystems){constmoduleSystem=awaitquestion(`WhichNode.jsmodulesystemdoyouwanttouse?(${moduleSystems.join("or")})`,{choices:moduleSystems,});returnmoduleSystem;}上面函数使用的 question 函数由 zx 提供。
现在我们将创建一个 getNodeModuleSystem 函数,以调用 promptForModuleSystem 函数。它将检查所输入的值是否有效。如果不是,它将再次询问:
asyncfunctiongetNodeModuleSystem(){constmoduleSystems=["module","commonjs"];constselectedModuleSystem=awaitpromptForModuleSystem(moduleSystems);constisValidModuleSystem=moduleSystems.includes(selectedModuleSystem);if(!isValidModuleSystem){console.error(chalk.red(`Error:Modulesystemmustbeeither'${moduleSystems.join("'or'")}'\n`));returnawaitgetNodeModuleSystem();}returnselectedModuleSystem;}现在我们可以通过运行 npm init 命令生成我们项目的 package.json 文件:
await社区精选|如何使用 zx 编写 shell 脚本-今日头条npminit--yes`;然后我们将使用 readPackageJson 辅助函数来读取新创建的 package.json 文件。我们将询问项目应该使用哪个模块系统,并将其设置为 packageJson 对象中的 type 属性值,然后将其写回到项目的 package.json 文件中:
constpackageJson=awaitreadPackageJson(targetDirectory);constselectedModuleSystem=awaitgetNodeModuleSystem();packageJson.type=selectedModuleSystem;awaitwritePackageJson(targetDirectory,packageJson);提示:当你用--yes 标志运行 npm init 时,要想在 package.json 中获得合理的默认值,请确保你设置了 npminit-*的配置设置 https://link.segmentfault.com/?enc=X0aVKO8sVtj0bgZhFmgjVw%3D%3D.hxYbJxX5X2odlcPpk5MdIVLkT7ESAsFMhFnFSRBftI2c2BjNTUCtV1gwdXnpxWGI 。
安装所需项目依赖
为了使运行我们的启动工具后能够轻松地开始项目开发,我们将创建一个 promptForPackages 函数,询问要安装哪些 npm 包:
asyncfunctionpromptForPackages(){letpackagesToInstall=awaitquestion("Whichnpmpackagesdoyouwanttoinstallforthisproject?");packagesToInstall=packagesToInstall.trim().split("").filter((pkg)=>pkg);returnpackagesToInstall;}为了防止我们在输入包名时出现错别字,我们将创建一个 identifyInvalidNpmPackages 函数。这个函数将接受一个 npm 包名数组,然后运行 npm view 命令来检查它们是否存在:
asyncfunctionidentifyInvalidNpmPackages(packages){$.verbose=false;letinvalidPackages=[];for(constpkgofpackages){try{await社区精选|如何使用 zx 编写 shell 脚本-今日头条npmview${pkg}`;}catch(error){invalidPackages.push(pkg);}}$.verbose=true;returninvalidPackages;}让我们创建一个 getPackagesToInstall 函数,使用我们刚刚创建的两个函数:
asyncfunctiongetPackagesToInstall(){constpackagesToInstall=awaitpromptForPackages();constinvalidPackages=awaitidentifyInvalidNpmPackages(packagesToInstall);constallPackagesExist=invalidPackages.length===0;if(!allPackagesExist){console.error(chalk.red(`Error:Thefollowingpackagesdonotexistonnpm:${invalidPackages.join(",")}\n`));returnawaitgetPackagesToInstall();}returnpackagesToInstall;}如果有软件包名称不正确,上面的函数将显示一个错误,然后再次询问要安装的软件包。
一旦我们得到需要安装的有效包列表,就可以使用 npm install 命令来安装它们:
constpackagesToInstall=awaitgetPackagesToInstall();consthavePackagesToInstall=packagesToInstall.length>0;if(havePackagesToInstall){await社区精选|如何使用 zx 编写 shell 脚本-今日头条npminstall${packagesToInstall}`;}为工具生成配置
创建项目配置是我们用项目启动工具自动完成的最佳事项。首先,让我们添加一个命令来生成一个.gitignore 文件,这样我们就不会意外地提交我们不希望在 Git 仓库中出现的文件:
await社区精选|如何使用 zx 编写 shell 脚本-今日头条npxgitignorenode`;上面的命令使用 gitignore https://link.segmentfault.com/?enc=V%2FIoxipE2WxmNECDRNKMqg%3D%3D.7Y4d34n%2BoUVlJkYEiJt3NLa9RHn2pYtKq%2BHpO67HIQjfjKAHIxcMynZzTudktHaV 包,从 GitHub 的 gitignore https://link.segmentfault.com/?enc=Szh56TKqT6gYmrJidM7ZXg%3D%3D.BK7Vf5F73GaplJKeN48OsobM6U4EPriatZOr3dBhsSVZIuIUiktyqfS1jhLLtQ7o 模板中拉取 Node.js 的.gitignore 文件。
为了生成我们的 EditorConfig https://link.segmentfault.com/?enc=dGD5%2BEyCTqLfqDSif7%2FHgQ%3D%3D.xgNWBjR6m%2FIUFlG1UmM3CYX2R%2BLXFN531lZhlvCRRWs%3D、Prettier https://link.segmentfault.com/?enc=n7clHFqLMZZala0zu1JjdQ%3D%3D.jivAg4cL8HkSAhuurMUHrlfhGGILOpveTQR0rjCRXyg%3D 和 ESLint https://link.segmentfault.com/?enc=J4jXEAmmBQ83%2FWSrOtTsAQ%3D%3D.b%2FKC0kT%2FZWZQ9KBItXDMQHWJBzc5aW9hUZsxWHBsMY4%3D 配置文件,我们将使用一个叫做 Mrm 的命令行工具。
全局安装我们需要的 mrm 依赖项:
npminstall--globalmrmmrm-task-editorconfigmrm-task-prettiermrm-task-eslint然后添加 mrm 命令行生成配置文件:
await社区精选|如何使用 zx 编写 shell 脚本-今日头条npxmrmeditorconfig`;await社区精选|如何使用 zx 编写 shell 脚本-今日头条npxmrmprettier`;await社区精选|如何使用 zx 编写 shell 脚本-今日头条npxmrmeslint`;Mrm 负责生成配置文件,以及安装所需的 npm 包。它还提供了大量的配置选项,允许我们调整生成的配置文件以符合我们的个人偏好。
生成 README
我们可以使用我们的 readPackageJson 辅助函数,从项目的 package.json 文件中读取项目名称。然后我们可以生成一个基本的 Markdown 格式的 README,并将其写入 README.md 文件中:
const{name:projectName}=awaitreadPackageJson(targetDirectory);constreadmeContents=`#${projectName}...`;awaitfs.writeFile(`${targetDirectory}/README.md`,readmeContents);在上面的函数中,我们正在使用 fs-extra 暴露的 fs.writeFile 的 promise 变量。
提交项目骨架
最后,是时候提交我们用 git 创建的项目骨架了:
await社区精选|如何使用 zx 编写 shell 脚本-今日头条gitadd.`;await社区精选|如何使用 zx 编写 shell 脚本-今日头条gitcommit-m"Addprojectskeleton"`;然后我们将显示一条消息,确认我们的新项目已经成功启动:
console.log(chalk.green(`\n✔️Theproject${projectName}hasbeensuccessfullybootstrapped!\n`));console.log(chalk.green(`Addagitremoteandpushyourchanges.`));启动新项目
mkdirnew-project./bootstrap-tool.mjs--directorynew-project并观看我们所做的一切。
总结
在这篇文章中,我们已经学会了如何在 Node.js 中借助 Google 的 zx 库来创建强大的 shell 脚本。我们使用了它提供的实用功能和库来创建一个灵活的命令行工具。
到目前为止,我们所构建的工具只是一个开始。这里有一些功能点子,你可能想尝试自己添加:
自动创建目标目录。如果目标目录还不存在,则提示用户并询问他们是否想要为他们创建该目录。
开源卫生。问问用户他们是否在创建一个将是开源的项目。如果是的话,运行命令来生成许可证和贡献者文件。
自动创建 GitHub 上的仓库。添加使用 GitHub CLI 的命令,在 GitHub 上创建一个远程仓库。一旦用 Git 提交了初始骨架,新项目就可以被推送到这个仓库。
本文中的所有代码都可以在 GitHub 上找到。
本文译自:https://link.segmentfault.com/?enc=5Z%2ByLQNgua%2FY5gnguxXllg%3D%3D.SeNINYETng%2BH6uZupDjhsYNRqM5GWB5jXVDlvMdMqsO9u3CmWfwe8DwT48iAvK%2Fsm8pflQ8jbxHc1sB281%2B6pA%3D%3D
作者:Simon Plenderleith
以上就是本文的所有内容。如果对你有所帮助,欢迎点赞、收藏、转发~
aep格式用什么打开编辑
记者 | 张熹珑
编辑 |
广东最新外贸成绩出炉。11月16日,据海关广东分署统计,今年前10个月,广东外贸进出口6.83万亿元,增长1.9%,以19.7%的占比稳居全国第一。其中,出口4.39万亿元,增长6.8%;进口2.44万亿元,下降5.9%。
贸易市场方面,东盟为广东第一大贸易伙伴。报告期内,广东对东盟进出口1.1万亿元,增长10%;对美国、欧盟和中国台湾进出口分别增长8.6%、8.2%和3%;对中国香港下降9%。
从贸易方式来看,前10个月,广东一般贸易进出口增长6.5%,增速较广东整体快4.6个百分点,占广东进出口总值的54.8%;加工贸易进出口下降2%,占25.8%;保税物流增长6%,占16.2%。
一般贸易方式进出口额的增长意味着更多本土品牌商品出口海外,企业拥有更多市场主动权和盈利空间。在产业结构进一步优化的过程中,广东的锂电池、新能源汽车和手机正加速出海。
“总的来看,广东外贸处于稳定缓慢恢复状态。总量仍保持全国第一,出口略好于进口,既体现了制造业大省的地位,也显现出内需不足的问题。” 广东省体制改革研究会执行会长彭澎表示。
锂电池和新能源汽车加速出海
新能源产品无疑是出口中表现最亮眼的。数据显示,1-10月,广东出口机电产品增长3.6%,占广东出口总值的66.6%。其中,电动载人汽车增长2.7倍,太阳能电池和锂离子蓄电池分别增长37.1%和47.8%。
彭澎告诉界面新闻记者,新能源产品大涨,这既是全国的现象,也体现广东在新能源市场中居重要地位。
一直以来,广东是国内重要的汽车市场和新能源基地。阿里巴巴国际站数据显示,前三季度,阿里巴巴国际站南粤大区的重点行业GMV(商品交易总额)均呈现增长趋势,其中新能源行业增长134.7%、汽车零配件行业增长27.6%、机械行业增长14.9%。
随着比亚迪、广汽埃安等龙头品牌壮大,以及小鹏汽车等新势力的崛起,走在广东新能源领域前头的两个城市——深圳、广州相关新能源产品的出口增速也远超省内平均水平。
前三季度,深圳市锂离子蓄电池出口400.2亿元,较去年同期增长75.2%。其中,9月份出口规模创下历史新高的62.4亿元,增长65.3%,自2020年7月起已连续27个月保持增长。
民营企业贡献了主要力量,出口占比近八成。前三季度,深圳市民营企业出口锂电池318.7亿元,增长75.9%,比重达到79.6%。
而同期广州出口锂离子蓄电池27.74亿元,同比增速41.2%;出口太阳能电池3.86亿元,同比增速57.6%。
除了两位“一哥”,珠三角甚至非珠地区在锂电池出口方面亦有不错的表现。例如佛山和肇庆前三季度锂电池出口分别为5.13亿元、8879.1万元,增速32.7%、42%;非珠三角地区的河源、韶关分别达到2.17亿元和5199.5万元,同比变动28.2%、32.7%。
近年来,随着消费电子产品、电动车、新能源等行业飞速发展,锂离子电池迅速成为电池市场首选,被广泛应用于相关场景。
现时国内锂离子电池商家集中在深圳、东莞、广州一带,也带动其线下产业布局。例如在新能源汽车领域,天眼查数据显示,目前广东存续、在业的新能源汽车企业达到7万余家。
比亚迪、广汽埃安的交付量长期“霸榜”。11月16日,比亚迪第300万辆新能源汽车下线发布,成为首个达成300万辆新能源汽车里程碑的中国品牌。
今年10月,比亚迪汽车销售21.8万辆,同比增长142%,单月销量再创历史新高,并已连续三个月登上国内新能源车销量排行榜榜首。根据公司财报,第三季度实现归母净利润达到57.16亿元,创单季新高。
广汽埃安也在快速追赶。1-10月,埃安累计交付量达到21.24万辆。11月3日,广汽埃安发布全新一代高端纯电专属平台AEP3.0,官方号称在平台技术方面全面超越特斯拉。
目前,广东新能源汽车加速“出海”,正加快抢占国际市场先机,也将进一步带动广东从汽车出口大省转变为新能源汽车出口大省。
以广州为例,前三季度,广州电动载人汽车出口8.13亿元,增速255.1%。其中纯电动乘用车出口4.73亿元,同比增长130%。纯电动客车和插电式混合动力乘用车呈爆发性增长,出口增速分别为7205%、1323.8%,金额分别达到2.36亿元、4027.3万元。
另一出口呈现旺盛趋势的产品为手机。前三季度,广东出口手机1792.2亿元,同比增长7.9%。
作为全国手机品牌基地,坐拥华为、OPPO、荣耀等手机品牌,深圳手机出口自然表现不菲。前三季度,深圳出口手机1116.2亿元,较去年同期增长34.4%。其中,9月份出口315.6亿元,增长83%,已连续6个月保持增势。
其中,深圳外商投资企业出口手机839.9亿元,增长36.5%,占75.2%;民营企业出口236.6亿元,增长42.3%,占21.2%。
不管是锂电池还是手机,香港都是重要的目的地之一。一直以来,香港都是内地锂离子电池最大的转口贸易地。对于深圳来说,香港亦是最大的手机出口市场。前9月深圳对香港出口手机848.9亿元,增长14.9%,比重达76%。
进口“冰火两重天”
相比出口额节节攀升的新能源产品,家用电器却有大幅下滑。前三季度广东家电出口额为2201.8亿元,同比变动-10.4%。据广东海关分析,由于上半年以来大宗商品价格上涨,导致家电企业经营成本上升,对家电企业毛利率影响较大。
国际形势和疫情是另一重影响因素。珠海科力通电器有限公司业务员钟晓恩告诉界面新闻记者,今年面向俄罗斯的出口有所下滑,美国关税也有影响:“目前十月份的产能跟去年差不多。本来计划今年要有增长,现在能维持跟去年差不多的销售额就不错了。”
科力通电器主营红酒开瓶器、胡椒研磨器、起泡器等小家电产品。面向中东、南亚、美加、俄罗斯和欧洲等地区出口。
疫情之下,国外和国内的贸易往来也困难重重。以科力通电器最重要的贸易平台之一广交会为例,今年广交会依旧线上举行,但效果不佳。
“我们发了邀请给老客户,但大家有时差,而且外国客户可能还没完全适应直播方式和隔着屏幕交流,点击量不高,互动也很少。”线上直播的效果并不好,钟晓恩提到,每场点击人数最多只有两位数。
而在进口方面,数据目前还未回暖。1-10月,广东进口2.44万亿元,下降5.9%。其中,进口农产品增长12.6%,进口机电产品下降8.2%。
整体机电产品进口下降趋势下,半导体制造设备进口增长45.2%,呈现“冰火两重天”之态。
彭澎认为,这说明了广东在高科技尤其是核心技术上仍有短板。而在普通机电产品方面,广东的优势已经比较明显,甚至是出口大省了,部分替代了传统产业如服装、玩具、塑料制品方面的出口,后者转移去东南亚较多。
“广东产业结构有一定上升调整,但主要优势是在中端技术产业,高端技术产业仍有上升空间,农产品、原材料对外依赖度仍然较大。”彭澎评价道。
(文中钟晓恩为化名)