How do I pipe input to a Node.js program that uses readline-sync? - node.js

I have a very simple Node.js program that uses readline-sync to accept input, then echo it to the console:
const readlineSync = require('readline-sync');
const input = readlineSync.prompt();
console.log(input);
It works fine as an interactive program; however, when I try to pipe input to it (in either Git Bash or PowerShell), I get a Node.js error:
PS> echo "1.2" | node .\index.js
Windows PowerShell[35552]: c:\ws\src\node_file.cc:1631: Assertion `(argc) == (5)' failed.
Adding a #!/usr/bin/env node shebang and running it as a script with echo "1.2" | .\script.js produces the same error.
Is there a configuration option or something that I'm missing that allows readline-sync to read input from a pipe? Is there something wrong with how I'm running it in the shell? Any advice would be appreciated.

It is most probably the package compatibility issue with the node version that you are using. You need to check all the dependencies whether they are compatible with the node version that you are using.

I think your program is taking 'input' in the form of 'argument' but not from 'stdin'.
when you use '|' , Input will be given to the program as 'stdin' not as 'argument'
so to convert 'stdin' coming out of '|' to 'input argument' we can use 'xargs' in linux.
try following on linux/bash to see if it works :
echo "1.2" | xargs .\script.js
Just to give a example , we can see the functionality of 'echo' command which can take 'input' only in the form of 'arguments' but not as 'stdin' :
# following command does print anything :
echo boo | echo
# but when xargs is used after | , It displays the output :
echo boo | xargs echo
boo

Related

How to execute svn command along with grep on windows?

Trying to execute svn command on windows machine and capture the output for the same.
Code:
import subprocess
cmd = "svn log -l1 https://repo/path/trunk | grep ^r | awk '{print \$3}'"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
'grep' is not recognized as an internal or external command,
operable program or batch file.
I do understand that 'grep' is not windows utility.
Getting error as "grep' is not recognized as an internal or external command,
operable program or batch file."
Is it only limited to execute on Linux?
Can we execute the same on Windows?
Is my code right?
For windows your command will look something like the following
svn log -l1 https://repo/path/trunk | find "string_to_find"
You need to use the find utility in windows to get the same effect as grep.
svn --version | find "ra"
* ra_svn : Module for accessing a repository using the svn network protocol.
* ra_local : Module for accessing a repository on local disk.
* ra_serf : Module for accessing a repository via WebDAV protocol using serf.
Use svn log --search FOO instead of grep-ing the command's output.
grep and awk are certainly available for Windows as well, but there is really no need to install them -- the code is easy to replace with native Python.
import subprocess
p = subprocess.run(["svn", "log", "-l1", "https://repo/path/trunk"],
capture_output=True, text=True)
for line in p.stdout.splitlines():
# grep ^r
if line.startswith('r'):
# awk '{ print $3 }'
print(line.split()[2])
Because we don't need a pipeline, and just run a single static command, we can avoid shell=True.
Because we don't want to do the necessary plumbing (which you forgot anyway) for Popen(), we prefer subprocess.run(). With capture_output=True we conveniently get its output in the resulting object's stdout atrribute; because we expect text output, we pass text=True (in older Python versions you might need to switch to the old, slightly misleading synonym universal_newlines=True).
I guess the intent is to search for the committer in each revision's output, but this will incorrectly grab the third token on any line which starts with an r (so if you have a commit message like "refactored to use Python native code" the code will extract use from that). A better approach altogether is to request machine-readable output from svn and parse that (but it's unfortunately rather clunky XML, so there's another not entirely trivial rabbithole for you). Perhaps as middle ground implement a more specific pattern for finding those lines -- maybe look for a specific number of fields, and static strings where you know where to expect them.
if line.startswith('r'):
fields = line.split()
if len(fields) == 13 and fields[1] == '|' and fields[3] == '|':
print(fields[2])
You could also craft a regular expression to look for a date stamp in the third |-separated field, and the number of changed lines in the fourth.
For the record, a complete commit message from Subversion looks like
------------------------------------------------------------------------
r16110 | tripleee | 2020-10-09 10:41:13 +0300 (Fri, 09 Oct 2020) | 4 lines
refactored to use native Python instead of grep + awk
(which is a useless use of grep anyway; see http://www.iki.fi/era/unix/award.html#grep)

Why pipes are not working on Powershell for various NodeJS CLI tools?

I am trying around the following highly used tools:
prettyjson
prettier
For example when I run the following on Powershell:
echo '{"a": 1}' | prettyjson
The terminal will just keep waiting for inputs till CTRL+C pressed and it exits with no expected output.
The workaround is to add .cmd to the command or just use cmd instead:
echo '{"a": 1}' | prettyjson.cmd
Outputs
a: 1
This seems to be a known limitation and a pull request is available:
https://github.com/npm/cmd-shim/pull/43

passing arguments to a node script coming from stdin

overview
I'd like to pass arguments to a node script coming from stdin.
generally, I'm shooting for something like this
nodeScript.js | node {{--attach-args??}} --verbose --dry-run
that would act the same as
node nodeScript.js --verbose --dry-run
more detail
here's a boiled down script for illustration, dumpargs.js
console.log("the arguments you passed in were");
console.log(process.argv);
console.log("");
so you could then:
node dumpargs.js --verbose --dry-run file.txt
[ 'node',
'/home/bill-murray/Documents/dumpargs.js',
'--verbose',
'--dry-run',
'file.js' ]
now the question, if that script comes in across stdin (say, via cat or curl)
cat dumpars.js | node
the arguments you passed in were
[ 'node' ]
is there a good way to pass arguments to it?
not node: with bash, using dumpargs.sh this time
echo "the arguments you passed in were"
printf "> $#"
echo
the answer would look like
cat dumpargs.sh | bash -s - "--verbose --dry-run file.txt"
the arguments you passed in were
> --verbose --dry-run file.txt
There is a specific syntax for this use case. The doc says :
- Alias for stdin, analogous to the use of - in other command line utilities, meaning
that the script will be read from stdin, and the rest of the options are passed to
that script.
-- Indicate the end of node options. Pass the rest of the arguments to the script.
If no script filename or eval/print script is supplied prior to this, then the next
argument will be used as a script filename.
So just do the following :
$ cat script.js | node - args1 args2 ...
For example, this will returns "hello world" :
$ echo "console.log(process.argv[2], process.argv[3])" | node - hello world
This isn't pretty, but works.
The call to node is going to launch the REPL, so your problem should be equivalent to setting / using argv manually from the terminal. Try doing something like:
// argv.js
process.argv[1] = 'asdf';
process.argv[2] = '1234';
and doing cat argv.js dumpargs.js | node.

Run external program from R expecting user input

I have an external program called GPOPSIM_for_linux that I would like to run from R. The program expects user input in form of the name of a parameter file. Suppose that MyParam.txt is its name.
Issuing printf 'MyParam.txt' | /home/domi89/GPOPSIM/GPOPSIM_for_linux in the shell works fine, but when I try
> cmd <- "printf 'MyParam.txt' | /home/domi89/GPOPSIM/GPOPSIM_for_linux"
> system2(command = shQuote(cmd))
sh: 1: "printf 'MyParam.txt' | /home/domi89/GPOPSIM/GPOPSIM_for_linux": not found
it doesn't work.
I suspect issue is with system2 that requires command and arguments to be separated. While with original system function you can use
system('ls -al')
with system2 syntax is
system2('ls', args = '-al')
I messed up with the working directory...
Also, as hinted by Pafnucy, I need to use system() instead of system2()
It now works:
system("cd ./data; printf 'MyParam.txt' | /home/domi89/GPOPSIM/GPOPSIM_for_linux")

Internal Variable PIPESTATUS

I am new to linux and bash scripting and i have query about this internal variable PIPESTATUS which is an array and stores the exit status of individual commands in pipe.
On command line:
$ find /home | /bin/pax -dwx ustar | /bin/gzip -c > myfile.tar.gz
$ echo ${PIPESTATUS[*]}
$ 0 0 0
working fine on command line but when I am putting this code in a bash script it is showing only one exit status. My default SHELL on command line is bash only.
Somebody please help me to understand why this behaviour is changing? And what should I do to get this work in script?
#!/bin/bash
cmdfile=/var/tmp/cmd$$
backfile=/var/tmp/backup$$
find_fun() {
find /home
}
cmd1="find_fun | /bin/pax -dwx ustar"
cmd2="/bin/gzip -c"
eval "$cmd1 | $cmd2 > $backfile.tar.gz " 2>/dev/null
echo -e " find ${PIPESTATUS[0]} \npax ${PIPESTATUS[1]} \ncompress ${PIPESTATUS[2]} > $cmdfile
The problem you are having with your script is that you aren't running the same code as you ran on the command line. You are running different code. Namely the script has the addition of eval. If you were to wrap your command line test in eval you would see that it fails in a similar manner.
The reason the eval version fails (only gives you one value in PIPESTATUS) is because you aren't executing a pipeline anymore. You are executing eval on a string that contains a pipeline. This is similar to executing /bin/bash -c 'some | pipe | line'. The thing actually being run by the current shell is a single command so it has a single exit code.
You have two choices here:
Get rid of eval (which you should do anyway as eval is generally something to avoid) and stop using a string for a command (see Bash FAQ 050 for more on why doing this is a bad idea.
Move the echo "${PIPESTATUS[#]}" into the eval and then capture (and split/parse) the resulting output. (This is clearly a worse solution in just about every way.)
Instead of ${PIPESTATUS[0]} use ${PIPESTATUS[#]}
As with any array in bash PIPESTATUS[0] contains the first command exit status. If you want to get all of them you have to use PIPESTATUS[#] which returns all the contents of the array.
I'm not sure why it worked for you when you tried it in the command line. I tested it and I didn't get the same result as you.

Resources