How to preserve quotes in a bash parameter? - node.js

I have a bash script (Mac OS X) that in turns calls a Node.js command line application.
I normally call the Node.js app like this:
node mynodeapp events:"Open project"
Which node has no problem parsing as one parameter, in spite of the space between "Open" and "project".
I call my bash script like this:
. mybashscript.sh 2014-03-20 "Open project"
Inside the bash script I have:
EVENTSQUOTES=\"$2\"
echo node mixpanel-extract date:$1 events:$EVENTSQUOTES
node mixpanel-extract date:$1 events:$EVENTSQUOTES
Running the script produces:
node mixpanel-extract date:2014-03-20 events:"Open project"
Parameters: { date: '2014-03-20',
events: [ '"Open' ] }
So although the echo output line looks fine, the Parameters: output from my Node.js app tells me that bash splits the parameter in two. I've also tried wrapping it in more quotes e.g. EVENTSQUOTES='\"$2\"' but it makes no difference.

You need to use quote while calling also:
node mixpanel-extract date:"$1" events:"$EVENTSQUOTES"

echo node mixpanel-extract "date:$1" "events:$2"
node mixpanel-extract "date:$1" "events:$2"
You need to quote the variable when you use it as well, otherwise word splitting will occur.

Related

Bash wrong spaces, quotes interpretation in variables

I see a weird behavior in shell scripts when I pass a variable with parameters to external ruby script
For example:
params="--val1=test --val2='test'"
ruby ./script.rb
causes ruby to output 'test' for var2 instead of test.
If I just pass params directly without using a variable everything works just fine.
Could you please clarify your question a bit?
From what I understand you have a shell script, something like:
#!/bin/bash
PARAMS="--val1=test --val2='test'"
ruby ./script.rb $PARAMS
And in script.rb you print out the value for the command line parameter val2. In this case it's expected that it prints out test instead of 'test', because the following steps are happening:
bash replaces $PARAMS with its value
bash tries to execute the line ruby ./script.rb --val1=test --val2='test'
now bash sees the quoted value 'test' and replaces it with test, so ruby / your script sees test

printf in nodejs execSync gives error - the system cannot find the file specified

In node when using ..
var child_process = require('child_process')
child_process.execSync("printf 'a'") // works fine
child_process.execSync("printf '<a>'") // throws the system cannot find the file specified
I get the error .. the system cannot find the file specified.
Anyone got any ideas how to fix this? I need to use printf. All I want to do is print <a>
I am using Windows GIT bash. Node v6.11.3
On the command line .. printf '<a>' works fine but printf \'<a>\' gives the same error.
Try swapping quote characters:
child_process.execSync('printf "<a>"')
With the quotes the other way around, the executing shell (which is cmd.exe in Windows, I believe) seems to think that you want to redirect a file called "a" into printf (like printf < a).

process input line by line with node CLI providing just an eval expression/callback to process each single line

I wonder about simple equivalent of perl -ne 'some expression' to be able to use node CLI possibly with --eval '<some expression, func/arrow>' and --require some-line-by-line-enabler. Is there any module making it possible or what can be approach to write one?
I've also found i.e. https://github.com/j-/require-cli and wonder if this may be the right way to go. I was trying it preparing some very basic module exposing forward of readline.on('line', callback) but consuming the stdin does not work just out of the box.
You can eval scripts with Node from command line:
node -e "console.log('Hello!')"

how to escape whitespace in Node-RED exec node command

Situation:
In my Node-RED flow there is a call to another program where I use the exec node.
The arguments are set-up in a function node and passed as the msg.payload to the exec node.
This works fine and returns the expected results as long as the command has no space in it.
Problem:
A customer cannot use the call to the command on his system, because the path tp the installation of the program contains a whitespace.
What I tried and didn't work:
Escaping the whitespace with ^ as read in another question: /opt/path^ toProgram^ directory/program
Escaping the whitespace with \\: /opt/path\\ toProgram\\ directory/program
Quoting the whole string with "": "/opt/path toProgram directory/program"
Quoting the whole string and escaping the whitespace: "/opt/path\\ toProgram\\ directory/program"
Quoting the whole string with '': '/opt/path toProgram directory/program'
Leaving the command line empty/""and combining any of the above points with its arguments to one string (in the function node that sets up the arguments for that exec node) and passing it on as the msg.payload -> input parameters in the exec node config panel. No Success.
What's not the problem:
The program itself works fine, on mine and on the customers system, it's only the path that is different
Other than that specific command string with whitespace, the configuration of the exec node and its msg.payload ( = input parameters or arguments) as well as the "use spawn() instead of exec()?" is fine and works
Request
Is there any other way to escape the whitespace that I'm not aware of so the path to the program can be found? From my understanding the whitespace is interpreted as a separator for the input arguments, which it should be, but not on the command string.
This should be a quick fix in my opinion, however nothing seems to work that usually works in node, js, php or bash.. thanks in advance for any hints and ideas!
Environment:
Node-RED version: v0.15.2 |
Node.js version: v5.12.0 |
Mac OS X 10.11.6 = Darwin 15.6.0 x64 LE
Screenshots
This is the part of the flow:
This config works:
This config does not work:
I've just tested this and option 3 (Quoting the path with "") works fine.
Putting "/home/foo/foo bar/date" in the command section of the exec node executed the binary correctly
After a good nights sleep the solution (at least what I thought at the time) was to adapt the exec node in node-red itself: The original code appended the additional arguments (the ones separated by whitespace) to the command, and then slice the whole string by whitespace into an array (node.cmd is the path to the command with or without whitespace):
before: var arg = node.cmd; arg += " "+msg.payload; arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
and the (earlier, incomplete) solution was to prepend the command with the builtin array.unshift(<value>) function to the array after the slice operation, so that the command path with the whitespace is not affected by the array creation:
after: var arg = ""; ...add msg.payload and slice arg string into array.. arg.unshift(node.cmd);
The file can be found at: nodered node 75-exec.js on github, in the lines 46-51
Update to real solution:
After further investigation it was clear, that the Use spawn() instead of exec()? option was the problem: the command part with whitespace in double quotes and using exec() worked fine. And my approach couldn't be used when the command path contained an argument, e.g. in /home/foo bar/date -r 1000.
Following the issue at the official github repository of node-red the command part to the arguments array at the beginning after the slice but instead to have the command path with whitespace in quotes and later unwrapping them, copying the behaviour when exec() is used. He added this line in the original file after line 51: if (/^".*"$/.test(cmd)) { cmd = cmd.slice(1,-1); } and now both spawn() and exec() work the same - credits to him, unfortunately I don't know his username here.

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.

Resources