How can I get the raw command line arguments in a node.js app,
given this example command:
node forwardwithssh.js echo "hello arguments"
process.argv will be [ "node", "/path/to/forwardwith.js", "echo", "hello arguments" ]
And there's no way to reconstruct the original echo "hello arguments" from that (ie. join(" " won't put quotes back).
I want the whole original raw string after the script name.
what I want is easily obtained in bash scripts with "$*", is there any equivalent way to get that in node.js?
Note: the intention in particular is to receive a command to be executed somewhere else (eg. thru ssh)
Wrap each of the args in single quotes, and escape and single quotes within each arg to '\'':
var cmd_string = process.argv.map( function(arg){
return "'" + arg.replace(/'/g, "'\\''") + "'";
}).join(' ');
That would give you a cmd_string containing:
'node' '/path/to/forwardwith.js' 'echo' 'hello arguments'
which can be run directly in another shell.
Related
TLDR; I am trying to find an equivalent to bash $# in autohotkey
I am developing an autohotkey script which will run a specific program when triggered.
<^#i::Run D:\Scripts\myprog.exe "arg_1" "arg_2"
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
I want the program to be started with specific command line arguments; that I provide during starting the script.
> ahk_prog.exe arg_1 arg_2
I basically want to be able to convert my command line arguments provided during starting the program to be able to run the Run Command every time I trigger it.
I tried this:
<^#i::Run D:\Scripts\myprog.exe %A_Args% ; A_Args is the array of CLI arguments
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
I also tried this(this one does not compile at all)
<^#i::Run D:\Scripts\myprog.exe "%*%" ; Trying Batch Like syntax since %1% and %2% are valid
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
Both of them do not work. Is there a way to do this?
A_Args(docs) is an array, so you'll have join it into a string.
For example:
for each, arg in A_Args
arg_list .= """" arg """ "
arg_list := RTrim(arg_list) ;trim trailing space (might be unnecessary)
MsgBox, % arg_list
Added quotes around the argument, since they will of course be needed if your argument has spaces in it.
Then you can add it to your Run command like so:
for each, arg in A_Args
arg_list .= """" arg """ "
arg_list := RTrim(arg_list) ;trim trailing space (might be unnecesary)
<^#i::Run, % "D:\Scripts\myprog.exe " arg_list
Could also be done as a one-liner like so:
<^#i::Run, % "D:\Scripts\myprog.exe " RTrim(Format(StrReplace(Format("{:0" A_Args.length() "}", ""), 0, """{}"" "), A_Args*))
I have this script called test.sh:
#!/bin/bash
STR = "Hello World"
echo $STR
when I run sh test.sh I get this:
test.sh: line 2: STR: command not found
What am I doing wrong? I look at extremely basic/beginners bash scripting tutorials online and this is how they say to declare variables... So I'm not sure what I'm doing wrong.
I'm on Ubuntu Server 9.10. And yes, bash is located at /bin/bash.
You cannot have spaces around the = sign.
When you write:
STR = "foo"
bash tries to run a command named STR with 2 arguments (the strings = and foo)
When you write:
STR =foo
bash tries to run a command named STR with 1 argument (the string =foo)
When you write:
STR= foo
bash tries to run the command foo with STR set to the empty string in its environment.
I'm not sure if this helps to clarify or if it is mere obfuscation, but note that:
the first command is exactly equivalent to: STR "=" "foo",
the second is the same as STR "=foo",
and the last is equivalent to STR="" foo.
The relevant section of the sh language spec, section 2.9.1 states:
A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.
In that context, a word is the command that bash is going to run. Any string containing = (in any position other than at the beginning of the string) which is not a redirection and in which the portion of the string before the = is a valid variable name is a variable assignment, while any string that is not a redirection or a variable assignment is a command. In STR = "foo", STR is not a variable assignment.
Drop the spaces around the = sign:
#!/bin/bash
STR="Hello World"
echo $STR
In the interactive mode everything looks fine:
$ str="Hello World"
$ echo $str
Hello World
Obviously(!) as Johannes said, no space around =. In case there is any space around = then in the interactive mode it gives errors as
No command 'str' found
I know this has been answered with a very high-quality answer. But, in short, you cant have spaces.
#!/bin/bash
STR = "Hello World"
echo $STR
Didn't work because of the spaces around the equal sign. If you were to run...
#!/bin/bash
STR="Hello World"
echo $STR
It would work
When you define any variable then you do not have to put in any extra spaces.
E.g.
name = "Stack Overflow"
// it is not valid, you will get an error saying- "Command not found"
So remove spaces:
name="Stack Overflow"
and it will work fine.
I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' ' and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var' as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']] – so nested strings – and program parses "$My Title$" (with escape sequences) differently than '$My Title$' (as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My will be replaced with an empty string, because the environment does not know any variable named $My and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
local safe_unquoted = "^[-~_/.%w%%+,:#^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\\]', '\\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '\''arg 1'\'' '\''$arg2'\'' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2 will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?" will NOT be expanded too (unless you explicitly set expand=true).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '\''set title '\''\'\'\''$My Title$'\''\'\'' arg1 arg2'
# index.js
console.log(process.argv) // expect this to print [.., .., '1']
# terminal
$ echo 1 | node index.js // just prints [.., ..]
What's the trick? How do I dynamically pass arguments to a node script from the command line via unix commands like echo, ls, ps aux, and so on?
Note: I see that I can read the output of unix commands from within my script using stdin, but what I'd like is to truly pass arguments to the script from the command line.
$ echo 1 | node index.js
In this command echo prints 1 to the standard output which is redirected (via pipe) to the standard input of the node command that accepts index.js argument. If you want to read the string printed by echo, read the standard input, e.g.:
var text = '';
process.stdin.setEncoding('utf8');
process.stdin.on('readable', function () {
var chunk = process.stdin.read();
if (chunk !== null) {
text += chunk;
}
});
process.stdin.on('end', function () {
console.log(text);
});
How do I dynamically pass arguments to a node script from the command line via unix commands like echo, ls, ps aux, and so on.?
With a pipe you can only redirect the bulk output from a command. You may use command substitution to pass the outputs of multiple commands as strings, e.g.:
node index.js --arg1="$(ls -ld /tmp)" --arg2="$(stat -c%a /tmp)"
Assign the output of the commands to shell variables in order to make your script more readable:
arg1="$(ls -ld /tmp)"
node index.js --arg1="$arg1"
A friend of mine showed me this:
$ node index.js `echo 1 2 3 4`
Actually does exactly what I want. This would result in:
// index.js
process.argv // [.., .., '1', '2', '3', '4']
The difference between this and #RuslanOsmanov answer is that the above will pass in the output as all the arguments to the node process, whereas:
$ node --arg1=`echo 1` --arg2=`echo 2`
Requires an individual command for each individual argument.
It would not work as expected with ls if your filenames contain spaces, as space characters are treated as argument delimiters.
See What does the backtick - ` - do in a command line invocation specifically with regards to Git commands? for more about this use of back ticks.
You’ve been using the process.stdout writable stream implicitly every time you’ve called console.log(). Internally, the console.log() function calls process.stdout.write() after formatting the input arguments. But the console functions are more for debugging and inspecting objects. When you need to write structured data to stdout, you can call process.stdout.write() directly.
Say your program connects to an HTTP URL and writes the response to stdout. Stream#pipe() works well in this context, as shown here:
var http = require('http');
var url = require('url');
var target = url.parse(process.argv[2]);
var req = http.get(target, function (res) {
res.pipe(process.stdout);
});
Before you can read from stdin, you must call process.stdin.resume() to indicate that your script is interested in data from stdin. After that, stdin acts like any other readable stream, emitting data events as data is received from the output of another process, or as the user enters keystrokes into the terminal window.
The following listing shows a command-line program that prompts the user for their age before deciding whether to continue executing.
To improve #Joseph's answer, you can use both command in between back ticks() and $(enter code here)` :
$ node --arg1=`echo 1` --arg2=$(echo 2)
I am new to shell scripts, I am trying to create a simple function which will return the concatenated two strings that are passed as parameters. I tried with below code
function getConcatenatedString() {
echo "String1 $1"
echo "String2 $2"
str=$1/$2
echo "Concatenated String ${str}"
echo "${str}"
}
//I am calling the above function
constr=$(getConcatenatedString "hello" "world")
echo "printing result"
echo "${constr}"
echo "exit"
I see the below output when running the script with above code,
printing result
String1 hello
String2 world
Concatenated String hello/world
hello/world
exit
If you look at the code I am first calling the function and then I am echoing "printing result" statement, but the result is first comes the "printing result" and echos the statement inside the function. Is the below statement calling the function
constr=$(getConcatenatedString "hello" "world")
or
echo ${constr}
is calling the function ?
Because if I comment out #echo ${constr} then nothing is getting echoed !!! Please clarify me.
The first is calling the function and storing all of the output (four echo statements) into $constr.
Then, after return, you echo the preamble printing result, $constr (consisting of four lines) and the exit message.
That's how $() works, it captures the entire standard output from the enclosed command.
It sounds like you want to see some of the echo statements on the console rather than capturing them with the $(). I think you should just be able to send them to standard error for that:
echo "String1 $1" >&2
paxdiablo's solution is correct. You cannot return a string from a function, but you can capture the output of the function or return an integer value that can be retrieved by the caller from $?. However, since all shell variables are global, you can simply do:
getConcatenatedString() { str="$1/$2"; }
getConcatenatedString hello world
echo "Concatenated String ${str}"
Note that the function keyword is redundant with (), but function is less portable.
A more flexible, but slightly harder to understand approach is to pass a variable name, and use eval so that the variable becomes set in the caller's context (either a global or a function local). In bash:
function mylist()
{
local _varname=$1 _p _t
shift
for _p in "$#"; do
_t=$_t[$_p]
done
eval "$_varname=\$_t"
}
mylist tmpvar a b c
echo "result: $tmpvar"
On my Linux desktop (bash-3.2) it's approx 3-5x faster (10,000 iterations) than using ``, since the latter has process creation overheads.
If you have bash-4.2, its declare -g allows a function to set a global variable, so you can replace the unpretty eval with:
declare -g $_varname="$_t"
The eval method is similar to TCL's upvar 1, and declare -g is similar to upvar #0.
Some shell builtins support something similar, like bash's printf with "-v", again saving process creation by assigning directly to a variable instead of capturing output (~20-25x faster for me).