问题
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