-print printing out string via npm but actually evaluating through powershell - node.js

The following script is defined in my package.json:
"abc": "node -p 'p=require(\"./package\");p.main=\"lib\";p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)'",
If I run 'npm run-script abc' I end up with a package.json with the string
'p=require(\"./package\");p.main=\"lib\";p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)'
If I run the command:
node -p 'p=require(\"./package\");p.main=\"lib\";p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)'
Directly in powershell I actually get the json output I'm looking for.
Not sure why the difference? I tried using -e/-eval as well to no avail, it seems to think that it just prints out the script?

Change your npm script to the following instead:
"abc": "node -p \"p=require('./package');p.main='lib';p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)\"",
Your npm script will now run succesfully via Powershell, Command Prompt (cmd.exe), Linux, and MacOS.
Changes are as follows:
The node/js code has been wrapped in JSON escaped double quotes \"...\" instead of single quotes.
The actual node/js code itself utilizes single quotes '...' instead of JSON escaped double quotes \"...\" because using escaped double quotes in JavaScript is invalid syntax.
"abc": "node -p \"p=require('./package');p.main='lib';p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)\"",
^^ ^ ^ ^ ^ ^^

It's a quoting issue.
The problem is that running npm run-script abc is not interpreted in powershell (which properly supports both single and double quotes), but in cmd.
You can either:
Replace single quotes with double quotes, and use proper escaping (although the result may be a bit ugly):
"abc": "node -p \"p=require(\\\"./package\\\");p.main=\\\"lib\\\";p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)\""
Use single quotes inside the javascript code and double qoutes to surround the script
"abc": "node -p \"p=require('./package');p.main='lib';p.scripts=p.devDependencies=undefined;JSON.stringify(p,null,2)\""

Related

Why does npm start ignore escaped backticks from argument?

I have a super simple index.js file that outputs the first argument it receives:
// Args 0 & 1 are /usr/bin/node and path to file
console.log(process.argv[2]);
When I run the following:
node index.js test``backticks
the output is testbackticks, and as expected, the backticks are ignored.
Calling this again, with escaping this time:
node index.js test\`\`backticks
the output is test``backticks.
My package.json has a start script like so:
"start": "node ./index.js"
Calling npm start -- test``backticks will result in them being ignored, just like with the node.js call.
However, calling this with escaping:
npm start -- test\`\`backticks
Also results in the same output, which is simply:
testbackticks
With the backticks ignored as well.
Escaping npm start with double or single quotes worked fine, it is just the backticks that are ignored.
Why does calling npm start and node produces different results and how come backticks can't be escaped in npm start ?
Lastly, how can I, if possible, pass a string with backticks as an argument to npm start ?

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.

How to use Unicode literals with the Node.js -e "evaluate script" commandline switch

Node.js has an -e commandline switch to evaluate code provided on the commandline rather than in a separate script file.
Oddly, I can't find official documentation for it online but the node executable self-documents it if you run node --help:
>node --help
Usage: node [options] [ -e script | script.js ] [arguments]
node debug script.js [arguments]
Options:
-v, --version print Node.js version
-e, --eval script evaluate script
-p, --print evaluate script and print result
Now there are several ways to use Unicode literals in JavaScript, all seemingly begin with \u.
But no matter how I try to quote or escape the string with the Unicode literal, the code always fails to execute. Both under official Node.js and also JXcore.
Node
>node -e console.log('hello \u00A9')
[eval]:1
console.log('hello
^^^^^^
SyntaxError: Unexpected token ILLEGAL
at Object.exports.runInThisContext (vm.js:53:16)
at Object.<anonymous> ([eval]-wrapper:6:22)
at Module._compile (module.js:435:26)
at node.js:578:27
at doNTCallback0 (node.js:419:9)
at process._tickCallback (node.js:348:13)
JXcore
>jx -e console.log('hello \u{00A9}')
SyntaxError: unterminated string literal ([eval] 1:12)
at ([eval]-wrapper:6:8)
at Module.prototype._compile (module.js:621:10)
at evalScript (node.js:1054:18)
at startup (node.js:419:7)
at node.js:1604:3
I've tried double \\ and quadruple \\\\. I've tried single and double quote characters to delimit the strings.
(I am only trying this under Windows, just in case this might work fine under *nix.)
Try
node -e "console.log('hello \u00A9')"
Double-quoting ensures that console.log('hello \u00A9') is interpreted as a single argument by node.
In your original attempt, node saw 2 arguments following -e (split by the space inside 'hello \u00A9'): console.log('hello and \u00A9'), and since the first argument following -e was not by itself a syntactically valid expression, you got a syntax error.
Background:
The scriptargument (-e's option-argument) containing the snippet of JavaScript code to execute must be a distinct argument on the command line, so that it can be distinguished from other arguments, such as the arguments to pass to it ([arguments]).
The way to make a string with embedded whitespace distinct as an argument is to quote it as a whole.
On Windows, this is typically done with double quotes, but it is ultimately up to the program invoked to interpret the arguments (after the shell may have expanded tokens, such as environment-variable references).
Since single- and double-quoting can be used interchangeably in JavaScript, you may even get away with swapping single- and double quotes: node -e 'console.log("hello \u00A9")'
(That definitely works on Unix, where the shell itself recognizes a single-quoted string as a distinct argument, and all programs are handed an array of parsed arguments rather than a command line to interpret themselves).

Multiword string as a curl option using Bash

I want to get some data from a HTTP server. What it sends me depends on what I put in a POST request.
What I put in the INPUT_TEXT field is a sequence of words. When I run the following command, I get good looking output.
$ curl http://localhost:59125/process -d INPUT_TEXT="here are some words"
I want a bash script to take some string as a command line argument, and pass it appropriately to curl. The first thing I tried was to put the following in a script:
sentence=$1
command="curl http://localhost:59125/process -d INPUT_TEXT=\"${sentence}\""
$command
I then run the script like so:
$ ./script "here are some words"
But then I get a curl Couldn't resolve host error for each of "are", "some", and "words". It would seem that "here" got correctly treated as the INPUT_TEXT, but the rest of the words were then considered to be hosts, and not part of the option.
So I tried:
command=("curl" "http://localhost:59125/process" "-d" "INPUT_TEXT='$sentence'")
${command[#]}
I got the same output as the first script. I finally got what I wanted with:
result=$(curl http://localhost:59125/process -d INPUT_TEXT="${sentence}")
echo $result
I'm still unsure as to what the distinction is. In the first two cases, when I echoed out the contents of command, I get exactly what I input from the interactive Bash prompt, which had worked fine. What caused the difference?
The following will work:
command=("curl" "http://localhost:59125/process"
"-d" "INPUT_TEXT=$sentence")
"${command[#]}"
That has two changes from yours:
I removed the incorrect quotes around $sentence since you don't want to send quotes to the server (as far as I can see).
I put double-quotes around the use of "${command[#]}". Without the double quotes, the array's elements are concatenated with spaces between them and then the result is word-split. With double quotes, the individual array elements are used as individual words.
The second point is well-explained in the bash FAQ and a bunch of SO answers dealing with quotes.
The important thing to understand is that quotes only quote when a command is parsed. A quote which is a character in a variable is just a character; it is not reinterpreted when the value of the variable expanded. Whitespace in the variable is used for word-splitting if the variable expansion is unquoted; the fact that the whitespace was quoted in the the command which defined the variable is completely irrelevant. In this sense, bash is just the same as any other programming language.

How to preserve quotes in a bash parameter?

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.

Resources