1+1=10

记记笔记,放松一下...

Node.js 学习笔记(二)

接前面

通过和python对比的方式,继续了解 Node.js。

先学习在nodejs下如何使用命令行参数、环境变量、标准输入、标准输出、标准出错、信号处理以及程序返回值,而后看看如何将js脚本程序打包成可执行程序。

控制台程序

既然 node.js 能用于编写控制台程序,那么就可以直接和Python、C、C++ 等语言进行直接对照。

不妨先列个表格,找找感觉:

功能 Node.js Python C
标准输入 process.stdin.on('data', (d) => console.log(d)); d = input() scanf("%s", d);
标准输出 console.log("Hello 1+1=10!"); print("Hello 1+1=10!") printf("Hello 1+1=10!\n");
标准出错 console.error("Error!"); print("Error!", file=sys.stderr) fprintf(stderr, "Error!\n");
命令行参数 console.log(process.argv.slice(2)); print(sys.argv[1:]) int main(int argc, char *argv[]) { ... }
环境变量读取 console.log(process.env.MY_VAR); print(os.environ['MY_VAR']) char* myVar = getenv("MY_VAR");
**信号处理 ** process.on('SIGINT', handler) signal.signal(signal.SIGINT, handler) signal(SIGINT, handler);
程序返回值 process.exit(112); exit(112) return 112; (在 main 函数中)

从表格中看到,在对标准输入stdin的处理上,Node.js和其他语言差异最大,当然可以理解,这个和Node.js是异步架构有关。

注意,本文测试环境如下:

1
2
node -v
v20.10.0

另外,本文只关注esm,不关注cjs。简单起见,后缀名都用.mjs。

例子1 - 命令行参数

很奇怪,官方手册提到,需要import process from 'node:process',网络上其他地方也有import process from 'process'写法,尽管上面都可以工作,但是我的测试结果是不需要import。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const args = process.argv.slice(2);

// Check if any arguments were passed
if (args.length === 0) {
    console.log("No arguments provided.");
} else {
    console.log("Arguments:");
    args.forEach((arg, index) => {
        console.log(`${index + 1}. ${arg}`);
    });
}

运行结果:

1
2
3
4
5
6
7
$ node .\mytest.mjs  a b c
Arguments:
1. a
2. b
3. c
$ node .\mytest.mjs
No arguments provided.

例子2- 标准输入

当使用 process.stdin 时,它表示正在监听标准输入流(stdin)。Node.js 将会一直等待输入,因此程序不会自动退出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Read data from standard input
process.stdin.on('data', (data) => {
    const input = data.toString().trim();

    // Write input data to standard output
    process.stdout.write(`You entered: ${input}\n`);
});

// Prompt the user for input
process.stdout.write('Please enter some content:\n');

结果如下:

1
2
3
4
5
6
$ node .\mytest.mjs
Please enter some content:
Hello 1+1=10
You entered: Hello 1+1=10
Hello dbzhang800
You entered: Hello dbzhang800

例子3- 标准输出和标准出错

这个东西简单直接,和其他语言没有明显差异。

1
2
3
4
5
process.stdout.write("This is stdout\n"); // Output: This is stdout
process.stderr.write("This is stderr\n"); // Output: This is stderr

console.log("This is stdout"); // Output: This is stdout
console.error("This is stderr"); // Output: This is stderr

例子4- 环境变量

这个代码也直接

1
2
const myEnvVar = process.env.MY_ENV_VARIABLE;
console.log(myEnvVar);

不过设置环境变量,在不同环境下风格各异,其中一个:

1
MY_ENV_VARIABLE=hello node src/mytest.mjs

但在powershell下:

1
2
$env:MY_ENV_VARIABLE="hello"
node src/mytest.mjs

例子5- 信号处理

比想象的要简单很多。作为测试,我们同时监听了stdin,不然程序没等我们触发信号就已经直接退出了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
process.on('SIGINT', () => {
    console.log('Received SIGINT signal. Exiting...');
    process.exit(0);
});

process.stdin.on('data', () => {
    // do nothing
});

console.log('Press Ctrl+C to send a SIGINT signal.');

结果

1
2
3
4
node .\mytest.mjs
Press Ctrl+C to send a SIGINT signal.
<Ctrl+C>
Received SIGINT signal. Exiting...

例子6- 返回值

直接使用exit是可以的

1
2
3
console.log("Hello 1+1=10, exit code is 112");

process.exit(112);

不过和环境变量类似,在控制台下,如何查看这个值,没有标准的东西

  • 一般:echo $?
  • Windows命令行:echo %ERRORLEVEL%
  • powershell:$LASTEXITCODE

也可以,在退出前,先设置好

1
2
process.exitCode = 112;
console.log("Hello 1+1=10, exit code is 112");

打包

了解控制台程序编写基础,已经可以用nodejs写一个简单的程序了。那么,如何打包成一个exe,以便于在没有开发环境的机台上运行?

感觉这个和Python差不多,打包方案有很多,坑也很多。

有多个第三方工具,用于将Node.js运行时、依赖以及脚本捆绑在一起,以实现:将Node.js应用程序编译成单个可执行文件的命令行工具。简单试试看:

  • pkg:失败
  • nexe:失败
  • electron:主要是GUI吧,未测试
  • SEA!!:官方出品,使用复杂。至少可以用
  • ...

测试过程如下:

pkg

pkg官方仓库已经设置标记为只读,并说明它已经废弃:

pkg has been deprecated with 5.8.1 as the last release. There are a number of successful forked versions of pkg already with various feature additions. Further, we’re excited about Node.js 21’s support for single executable applications. Thank you for the support and contributions over the years. The repository will remain open and archived.

  • https://github.com/vercel/pkg

全局安装

1
npm install -g pkg

尝试使用

直接敲命令:

1
pkg mytest.mjs --output mytest.exe

结果,报错

1
2
3
4
5
pkg .\mytest.mjs --output mytest.exe
> pkg@5.8.1
> Targets not specified. Assuming:
  node20-win-x64
> Error! No available node version satisfies 'node20'

nexe

  • https://github.com/nexe/nexe

安装

1
npm install -g nexe

尝试使用

直接敲命令:

1
npx nexe -i .\mytest.mjs -o mytest.exe

结果,报错

1
2
3
4
5
npx nexe -i .\mytest.mjs -o mytest.exe

Error: Entry file ".\mytest.mjs" not found!

See nexe -h for usage..

SEA( single executable applications)

  • 先准备配置文件:sea-config.json
1
2
3
4
{
    "main": "mytest.mjs",
    "output": "sea-prep.blob"
}
  • 将要打包的js程序生成一个blob,用于注入到最终的程序中
1
node --experimental-sea-config sea-config.json
  • 拷贝 node 可执行程序到当前文件夹下,并重命名为我们要的名字,在powershell下,可以用
1
node -e "require('fs').copyFileSync(process.execPath, 'mytest.exe')" 
  • 将前面的blob注入到这个exe中
1
2
npx postject mytest.exe NODE_SEA_BLOB sea-prep.blob `
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
  • 运行一下(可以运行,只不过有提示信息):
1
2
3
4
.\mytest.exe
Hello 1+1=10
(node:22752) ExperimentalWarning: Single executable application is an experimental feature and might change at any time
(Use `mytest --trace-warnings ...` to show where the warning was created)

参考

  • https://nodejs.org/api/process.html
  • https://nodejs.org/docs/latest-v20.x/api/single-executable-applications.html