0

I tried to use Deno as a replacement for shell script, but got stuck.

I attempted to use Deno/Typescript to carry out the equivalent job as this:

docker run \
  -d \
  -v pgdata:/var/lib/postgresql/data \
  --name pg \
  -e POSTGRES_PASSWORD=123456 \
  --rm \
  -p 5432:5432 \
  postgres

ts code looks like this:

function runCmd(s: string[]): Deno.Process {
    return Deno.run({ cmd: s, stdout: "piped", stderr: "piped" })
}

function runPg() {
    const cmd = [
        "docker", 
        `run -d -v ${VOLUME}:/var/lib/postgresql/data --name pg -e POSTGRES_PASSWORD=${PASSWORD} --rm -p 5432:5432 postgres`
    ];

    return runCmd(cmd);
}

add execution bit to this ts file and run it in terminal:

error message

after this, I tried

function runPg() {
    const cmd = [
        "docker", 
        "run",
        `-d -v ${VOLUME}:/var/lib/postgresql/data --name pg -e POSTGRES_PASSWORD=${PASSWORD} --rm -p 5432:5432 postgres`
    ];

    return runCmd(cmd);
}

move out subcommand run from command options.

I got this: error message

I guess that Deno.run doesn't simply concatenate the passed-in string of command particles, but I cannot find enough information on this subject in order to fix the issue.

I haven't gone through the rust source code on this API, but I thought it's better to ask for help before trying the hard way.

Bing-hsu Gao
  • 343
  • 3
  • 14

2 Answers2

1

You need to specify each part of the command as a separate string in the cmd array:

function runPg() {
    const cmd = [
        "docker", 
        "run",
        "-d",
        "-v",
        `${VOLUME}:/var/lib/postgresql/data`,
        "--name",
        "pg",
        "-e",
        `POSTGRES_PASSWORD=${PASSWORD}`,
        "--rm",
        "-p",
        "5432:5432",
        "postgres"
    ];

    return runCmd(cmd);
}

This will send run as the first argument to docker instead of sending run -d … as the first argument.

You can also build your command as a single string and then use split(" ") as long as no arguments contain spaces.

mfulton26
  • 29,956
  • 6
  • 64
  • 88
  • Thank you for the answer. It works. Could you, if possible, elaborate on the underlying mechanism about how arguments are passed down to the OS? or Shell? Say, how is `["echo", "1", "2"]` different from `["echo", "1 2"]` under this context. – Bing-hsu Gao Aug 23 '21 at 17:03
  • 2
    I've seen this with other languages too. `["echo", "1", "2"]` is `echo 1 2` while `["echo", "1 2"]` is `echo 1\ 2`. The first sends two arguments to `echo` while the second sends only one. As such, `docker run -d\ -v…` sends only two arguments to `docker` (and therefore only one argument to `docker run`) whereas splitting it up sends many arguments. I hope that helps. – mfulton26 Aug 24 '21 at 02:08
0

a follow up on my trial-n-error journey on this top.

While reading a book about unix shell programming, it points out a way to help the shell differentiate the space in identifier and the space as delimiter. When one tries to cat a file named a b (there is a space in between), the command should be cat a\ b or, using quotes, cat 'a b'.

This gives me an idea why my command does not work in Deno. See, each item in the cmd string list is an identifier, when I mix up delimiter-space with identifiers, the command is interpreted in the wrong way.

For example.

If I'd like to cat a file named a b. In Deno.run, I need to use { cmd: ["cat", "a b"] }.

If I'd like to cat two files named a and b. In Deno.run, I need to use { cmd: ["cat", "a", "b"] }.

Just remember that space in a command particle counts as a part of that term.

Bing-hsu Gao
  • 343
  • 3
  • 14