How do I create a globally usable shell script using Nodejs rather than bash? - node.js

Basically, I want to be able to open a terminal and type
$ haylp
and have that return some helpful commands that I keep forgetting. I wanna do this without having to learn bash. or some other language because I know JavaScript. How can I do this without having to compile it or translate it into bash or anything? I just want to write a javascript file and have it run as if i wrote a shell script and put it in /usr/local/bin.

I figured it out and I just want people to know how to do it.
First, simple example- I wrote the text file I wanted to reference. Just a normal text or markdown file. I called it haylp.md
haylp.md:
commands i keep forgetting
Then I created a Node/JavaScript file called haylp.js to read the contents of it:
haylp.js
const fs = require('fs');
fs.readFile('/usr/local/lib/haylp/emacsCmds.md', 'utf8', (err, data) =>{
if(err){ console.error(err); return; }
console.log(data);
});
and this is where I got confused because I KNOW there's a way to just use a shebang that looks like "#!/bin/node" that would allow you to just type
$ haylp (or) ./haylp.js (or) sh ./haylp.js
So this answered my question about the shebang but I still couldnt get it to execute and also didn't know where to put it. So here's the full result.
Step 1 create the js file and add the 2 line shebang for compatibility.
haylp.js:
#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$#"
const fs = require('fs');
fs.readFile('/usr/local/lib/haylp/emacsCmds.md', 'utf8', (err, data) =>{
if(err){ console.error(err); return; }
console.log(data);
});
Step 2 Create the text file
Place it in a folder in /usr/local/lib/haylp (I'm not positive if this is the right place to put it; someone correct me if i'm wrong)
Step 3 Make the js file executable:
$ chmod +x haylp.js
Step 4 Copy haylp.js to bin
$ sudo mv ./haylp.js /usr/local/bin/haylp
Is this overkill for my usecase? Probably. But it might not be for yours.
It's probably not perfect so if anyone has suggestions, I'm all ears.

Related

Trying to run a python script from nodeJS and can't figure out the argument list

I have to run a python script from my node project that converts a .csv file to a .txt file.
The command for running the python script on the terminal is
python3 csv_to_rdf.py "pathToFile" "date" > "nameOfNewFile.txt"
eg. python3 csv_to_rdf.py modifiedCSV.csv 08122022 > test.txt
I am using the child_process.spawn() method in nodeJS to run the script but I can't figure out the args.
When I use the following code snippet:
I get the following error message:
My question is how do I send in "> test.txt" in my spawn() method?
This is incredible, I am a semantic developer, and I had an extremely similar file (a python script that parsed a .csv and then converted it to .txt and then to .TriG) and I needed it run in my Node.js backend.
Short answer
A very simple solution I thought of after writing everything below is:
Python is better at writing to files than bash. Use python to write to your file and then do not pass a redirect into your spawn() and you'll be good. Escaped characters are difficult to handle in the child_process because you have to deal with JavaScript and Python both trying to handle escaped characters at the same time. If python doesn't have write access, then below may server you better.
Long answer
I moved away from child processes because they cause security issues. Any child_process with user input can be exploited. If you could move away from a child process and re-write your python file in JS/TypeScript, that would be best. If you are confident in the use of the spawn, then here is a solution:
Firstly, you cannot add a redirect to your spawn because the command is python3 and, as the error suggests, python3 takes the redirect as an unrecognized argument. It literally takes it as an argument to the python file, so sys.argv is the array you passed in the spawn. Python will take it in but without seeing your python code, I couldn't say how the extra arguments are being handled. Obviously an error is being thrown.
Remember the child_process.spawn() happens asynchronously, so the stdout and stderr can be extracted right after. Read child_process.spawn() docs. So remove the redirect in the spawns argument array and just run another child_process or use fn to write the output to a file when the stdout occurs:
const { spawn } = require('node:child_process');
const childPython = spawn('python3', ['modifiedCSV.csv', '08182022']);
childPython.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
Or you could use exec instead.
const { exec } = require('node:child_process');
const path = "modifiedCSV.csv";
const date = "08182022";
exec(`python3 ${path} ${date} > test.txt`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
})
The main issue is, if the path to the modified csv file is in anyway connected to a user's input, you have a security risk, because if python3 is run as sudo, which I believe in node, it is, a user can inject python code in-place of the file path and re-write and encrypt all the files that it can touch.

child_process gives 'Command failed' executing grep

I want to search for existence of a file using child_process in node:
const { exec } = require('child_process');
exec('ls | grep "filename"', (err, result) => {...})
When the filename exists, exec result is fine. But when the filename doesn't exist, I get an error:
Command failed: ls | grep "filename"
In this case, how can I tell if it's an error executing the command, or just because no result is found?
EDIT
Thanks for the advice on not searching for a file this way. The above code is not the actual code, but just a demo piece illustrating my problem with grep. In my actual case I'm searching for keywords in the output by task spooler, thus I used exec and tsp -l | grep ...
You can determine this by looking at the value of err.code in the callback. It is documented in more detail in the Node.js docs.
Since the last command in the pipe is grep, consult the grep manpage for a complete list of status codes to branch your logic appropriately. In your case, grep will return a status code of 1 if no lines were selected (i.e. "there are no results"), so if err.code === 1, then you know no files were matched.
Note: as mentioned by #CharlesDuffy, It should be preferred to achieve your desired file manipulations via the Node.js File System API. Leverage this answer as an alternative for situations where exec is explicitly needed.

NodeJS Specific Language Syntax Checker

I recently found a npm package called syntax-checker (https://www.npmjs.com/package/syntax-checker)
And i would like to integrate this into my js script. I'm using a Discord chat bot which checks the message for a code block and the coding language. As the description of Syntax checker says, it supports Ruby, PHP, Perl, Lua, C/CPP, Bash, Javascript and Python. How would i integrate this into the bot? I currently use for js checking this script
if (message.content.includes("```js"))
{
let code = message.content.substring('```js '.length);
var codebegin = code.split("```js").pop();
var n = codebegin.indexOf('```');
var codeend = codebegin.substring(0, n != -1 ? n : codebegin.length);
var check = require('syntax-error');
var err = check(codeend);
if (err)
{
message.reply("Your code contains errors! ```" + err + "```");
}
else
{
message.reply("No Errors!");
}
}
Syntax-checker works by running the program on your computer used to compile code (with no output) and checking to see if there are any errors. It runs by analyzing every file in a directory passed in to it and then outputting to a file. You'll need to create a temporary file for each request then run the program using shell (look into child_process or exec for this).
All that module does ultimately is decide what language the code is from its file extension and run something like exec('php -l file/path/here.php', callbackFunctionHere). That's what it runs for PHP, the others are ruby -c, python -m py_compile, perl -c, luac -p, bash -n. gcc -fsyntax_only, and uglifyjs -o /dev/null.
With that knowledge, there's no sense in messing around with the file system whatsoever. Just use something like exec("echo '" + codeStr + "' | php -l', callbackFunctionHere);. Replace php -l with whichever linter you need. Make sure you escape any single quotes that might occur in the codeStr since you'll end up with odd errors otherwise.

Nodejs get file extension

I have a folder with images and they can have different formats but the name will always be unique. Is there a way to get the file extension if I know the file's name without the extension (eg. index and not index.html)? I could just check if the file exists for every extension I expect to be there but that seems like a bad solution to me.
Example:
I know there is an image called PIC but I don't know the extension (could be either '.png', '.jpg' etc.) therefore I can not use the file command.
Well, if your running Unix based systems, this could be a workaround.
var sys = require('util')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) {
console.log(stdout)
}
// this is where you should get your path/filename
var filename = "login";
// execute unix command 'file'
exec("file " + filename, puts);
I tested it for a PNG file and an EJS file, both with no extensions (what wouldn't make difference).
The results are below:
PNG:
photo: PNG image data, 100 x 100, 8-bit/color RGB, non-interlaced
EJS (what's basically a HTML):
login: HTML document, ASCII text
You can check file command line parameters to make it easier to work with the string (e.g. file -b filename).
If using Windows, then you'd have to check an alternative command for file.
Hope it's somehow useful.
I know I'm late to the party, but you can use the grep command in unix based systems.
ie ls | grep PIC. What this does is first give a directory listing of the working directory, and then searches for the phrase PIC from the output of the directory listing and prints it. (so the only thing that will be printed is the filename)
In Windows, use dir PIC.* /b
You can execute these commands using child_process as shown in other answers
var path = require('path')
path.extname('index.html')
// returns
'.html'
Here is the referenced answer
Node.js get file extension
Updated :
https://www.npmjs.com/package/file-extension
npm install --save file-extension
allows you to specify a filename then it will return the extension

How do I set up a preload file for node?

Is there a way to preload some file before each time I run node (interactively), just like .vimrc, .bash_profile, etc.?
I use node mainly interactively, and I use the module CSV a lot, is there a way to avoid typing require('./csv') every time I start node?
Create an initialization file (for example ~/.noderc):
var csv = require('csv');
// put a blank line at the end of the file
Now add this line to your shell config (.bashrc / .zshrc / whatever shell you use):
alias nodei="cat ~/.noderc - | node -i"
VoilĂ !
#Ilan Frumer provided one way to do it. I think I'll give another choice here: build a REPL of your own.
From their documentation. You can find a way to write a repl of your own. You can add whatever scripts before and after the interations of it, and even use some advance API's.
For example, I created a file called .noderc.js under ~ as follows
repl = require('repl');
myFunc = function(){
console.log("Hello, there!");
};
repl.start("> ");
And you can go ahead and alias nodei="node ~/.noderc.js",
$ nodei
> myFunc()
Hello, there!
undefined

Resources