如何使用 Deno.Run 在 Deno 中重新创建等同于 linux bash 的语句

How to re-create an equivalent to linux bash statement in Deno with Deno.Run

如何在 Deno 中重新创建等同于以下 Linux bash 语句的语句?

docker compose exec container_name -uroot -ppass db_name < ./dbDump.sql

我尝试了以下方法:

    const encoder = new TextEncoder
    const p = await Deno.run({
        cmd: [
            'docker',
            'compose',
            'exec',
            'container_name',
            'mysql',
            '-uroot',
            '-ppass',
            'db_name',
        ],
        stdout: 'piped',
        stderr: 'piped',
        stdin: "piped",
    })
    
    await p.stdin.write(encoder.encode(await Deno.readTextFile('./dbDump.sql')))
    await p.stdin.close()
    await p.close()

但出于某种原因,每当我这样做时,我都会收到一个错误 ERROR 1064 (42000) at line 145: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version,当我在 bash.

中执行完全相同的命令时,该错误不会发生

有人可以向我解释一下必须如何正确完成吗?

首先,注意几点:

  1. Deno 目前不提供创建分离子流程的方法。 (您没有提到这一点,但鉴于典型的 docker 组合用法,它似乎可能与您的场景相关)请参阅 denoland/deno#5501.

  2. Deno 的子流程 API 目前正在返工中。参见 denoland/deno#11016

其次,这里是相关文档的链接:

现在,这里以您的场景为例,详细说明了如何创建子流程(根据当前 API):

module.ts:

const dbUser = 'actual_database_username';
const dbPass = 'actual_database_password'
const dbName = 'actual_database_name';

const dockerExecProcCmd = ['mysql', '-u', dbUser, '-p', dbPass, dbName];

const serviceName = 'actual_compose_service_name';

// Build the run command
const cmd = ['docker', 'compose', 'exec', '-T', serviceName, ...dockerExecProcCmd];

/**
 * Create the subprocess
 *
 * For now, leave `stderr` and `stdout` undefined so they'll print
 * to your console while you are debugging. Later, you can pipe (capture) them
 * and handle them in your program
 */
const p = Deno.run({
  cmd,
  stdin: 'piped',
  // stderr: 'piped',
  // stdout: 'piped',
});

/**
 * If you use a relative path, this will be relative to `Deno.cwd`
 * at the time the subprocess is created
 *
 * https://doc.deno.land/deno/stable/~/Deno.cwd
 */
 const sqlFilePath = './dbDump.sql';

// Write contents of SQL script to stdin
await p.stdin.write(await Deno.readFile(sqlFilePath));

/**
 * Close stdin
 *
 * I don't know how `mysql` handles `stdin`, but if it needs the EOT sent by
 * closing and you don't need to write to `stdin` any more, then this is correct
 */
p.stdin.close();

// Wait for the process to finish (either OK or NOK)
const {code} = await p.status();
console.log({'docker-compose exit status code': code});

// Not strictly necessary, but better to be explicit
p.close();

没有示例输入文件,就无法确定您的确切问题。

尽管考虑到上下文,我怀疑您的输入文件对于单个 proc.stdin.write() 调用来说太大了。尝试使用 the writeAll() function 以确保完整的有效负载通过:

import { writeAll } from "https://deno.land/std@0.119.0/streams/conversion.ts";
await writeAll(proc.stdin, await Deno.readFile(sqlFilePath));

为了显示此修复的内容,这里有一个 Deno 程序 pipe-to-wc.ts,它将其输入传递给 Linux 'word count' 实用程序(在字符计数模式下):

#!/usr/bin/env -S deno run --allow-read=/dev/stdin --allow-run=wc
const proc = await Deno.run({
  cmd: ['wc', '-c'],
  stdin: 'piped',
});

await proc.stdin.write(await Deno.readFile('/dev/stdin'));
proc.stdin.close();
await proc.status();

如果我们使用这个程序,输入较小,则计数为:

# use the shebang to make the following commands easier
$ chmod +x pipe-to-wc.ts

$ dd if=/dev/zero bs=1024 count=1 | ./pipe-to-wc.ts
1+0 records in
1+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.000116906 s, 8.8 MB/s
1024

但是一旦输入很大,只有65k字节通过!

$ dd if=/dev/zero bs=1024 count=100 | ./pipe-to-wc.ts
100+0 records in
100+0 records out
102400 bytes (102 kB, 100 KiB) copied, 0.0424347 s, 2.4 MB/s
65536

要解决此问题,让我们将 write() 调用替换为 writeAll():

#!/usr/bin/env -S deno run --allow-read=/dev/stdin --allow-run=wc
const proc = await Deno.run({
  cmd: ['wc', '-c'],
  stdin: 'piped',
});

import { writeAll } from "https://deno.land/std@0.119.0/streams/conversion.ts";
await writeAll(proc.stdin, await Deno.readFile('/dev/stdin'));
proc.stdin.close();
await proc.status();

现在所有字节都在大输入上传递 :D

$ dd if=/dev/zero bs=1024 count=1000 | ./pipe-to-wc.ts
1000+0 records in
1000+0 records out
1024000 bytes (1.0 MB, 1000 KiB) copied, 0.0854184 s, 12.0 MB/s
1024000

请注意,一旦输入超过程序可用的内存量,这仍然会在大量输入时失败。 writeAll() 解决方案应该可以达到 100 兆字节左右。在那之后,您可能想切换到流媒体解决方案。