Get argument from pipe but also run prompts?

本小妞迷上赌 提交于 2020-05-16 21:59:52

问题


I'm writing a Node script designed to be executed from Bash terminal. It takes a couple of filenames and then asks questions to the user via a prompt about how to process them. I'm using yargs for parsing the command line arguments and prompt-sync for asking the user questions and that all seems to work fine...

Except when I pipe an argument to my script like so:

echo "file2.md" | .myscript.js --f1 file1.md

This works in so far as it compares file1.md and file2.md and I'm using pipe-args in conjunction with yargs to get that far. But when it comes to the prompts, having the piped argument interferes and stops them from working.

I've tried taking this outside my script and testing it in isolation, using both prompt-sync and inquirer and piping an argument into script seems to also affect the prompt.

So I'm now pretty certain it's not the specific prompt package.


To recreate:

Here's my test for prompt-sync (file called prompt-sync-test.js):

#!/usr/bin/env node
const prompt = require("prompt-sync")({"sigint": true})

for (var i = 0; i < 5; i++) {
  console.log("Going to bring up a prompt")
  var result = prompt("This is a test -- enter something >");
  console.log("Result", result)
}

...when it works:

Running ./prompt-sync-test.js works fine and asks five questions printing the results each time.

$ ./prompt-sync-test.js
Going to bring up a prompt
This is a test -- enter something >1
Result 1
Going to bring up a prompt
This is a test -- enter something >2
Result 2
Going to bring up a prompt
This is a test -- enter something >3
Result 3
Going to bring up a prompt
This is a test -- enter something >4
Result 4
Going to bring up a prompt
This is a test -- enter something >5
Result 5

...when it doesn't:

But running echo hello world | ./prompt-sync-test.js prints the message for the first prompt but then any input entered just repeats the prompt message again, with any previous answers entered, like this (where I'm entering the same numbers as in the test described above and hitting enter after each one)...

$ echo hello world | ./prompt-test.
js
Going to bring up a prompt
This is a test -- enter something >1
This is a test -- enter something >1
                                     2
This is a test -- enter something >1
2
                                       3
This is a test -- enter something >1
2
3
                                         4
This is a test -- enter something >1
2
3
4
                                           5
This is a test -- enter something >1
2
3
4
5

Piping something into my script seems to interfere with the prompt itself.

Basically, I want to be able to use what's piped in in my script but have the prompt work as in the first case. How do I get this to happen?


回答1:


I found a solution using a combination of packages, namely prompts and ttys.

I switched to prompts because it allows you to configure a variable called stdin for each question which means you can decide where to take input from.

Most prompt packages seem to default to using process.stdin as the input. This is normally fine but when you pipe data into a command, process.stdin is occupied by the pipe, hence the problem I was having before. However, if you know that process.stdin is occupied by the pipe you can still get keyboard input from the user.

This is where ttys comes in. It's a very simple package which checks whether pipe has been used or not.

I can almost recreate the successful result I outlined in my question with the following script -- the only thing missing is the pre-prompt message (Going to bring up a prompt). And I've added pipe-args and yargs to show that the piped in data is preserved.

#!/usr/bin/env node
const prompts = require("prompts");
const tty = require("tty");
const pipe = require("pipe-args").load();
const argv = require("yargs").argv;
const ttys = require("ttys");

const questions = [0, 1, 2, 3, 4]
  .map(x => ({
    type: "text",
    name: `prompt-${x}`,
    message: "This is a test -- enter something",
    stdin: ttys.stdin
  }));


const onSubmit = function (prompt, answer) {
  console.log("Result", answer); // => { value: 24 }
};

(async () => {
  const response = await prompts(questions, { onSubmit });
  console.log(argv);
  ttys.stdin.destroy();
})();



回答2:


You can use cat after in a subshell, like this:

(echo "file2.md"; cat) | .myscript.js --f1 file1.md
(echo hello world; cat) | ./prompt-test.js

You can also add exec to do it with one less process:

(echo "file2.md"; exec cat) | .myscript.js --f1 file1.md
(echo hello world; exec cat) | ./prompt-test.js

You can also put cat on the outside and use -:

cat <(echo "file2.md") - | .myscript.js --f1 file1.md
cat <(echo hello world) - | ./prompt-test.js


来源:https://stackoverflow.com/questions/61487593/get-argument-from-pipe-but-also-run-prompts

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!