How can I change actual shell cd with nodejs - node.js

I am creating a command line program to act as a shortcut for my environment. Commands like $ cd $enterprise/products/<product> are so common, that I like to compile into: $ enterprise product.
My problem is: I cannot change shell directory directly, like running $ cd $enterprise/products/<product> with process.chdir.
Using console.log(process.cwd()) shows that the directory has changed, but not on shell, only on nodejs internal process(I think it's running on a own shell).
Typing $ pwd in shell shows that I still are on the same folder.
I was searching for a solution, like a shell script that interprets the output of a nodejs file, then source the output.
Thanks.

This is actually bit trickier than it sounds.
You can't just change the working directory of the shell that is running the process, without making assumptions about which shell it is or the OS (I personally use applescript to spawn new terminal tabs).
What we can do, however, is spawn a new shell!
let child_process = require('child_process');
child_process.spawn(
// With this variable we know we use the same shell as the one that started this process
process.env.SHELL,
{
// Change the cwd
cwd: `${process.cwd()}/products/${product_id}`,
// This makes this process "take over" the terminal
stdio: 'inherit',
// If you want, you can also add more environment variables here, but you can also remove this line
env: { ...process.env, extra_environment: 'some value' },
},
);
When you run this, it seems like you cd into a directory, but actually you are still running inside nodejs!

You can't do this; every child process has its own working directory inherited from the parent. In this case, your cd gets its working directory from its parent (your shell). A child process can't change the directory – or any other state – of the parent process.

Related

"cd" has no effect when executing series of commands

I want to make a small CLI kinda project where I can execute series of commands. So, I wrote this code:
let commands = ["cd C:\\Users\\MegaMind\\Documents\\dynamodb_local_latest","dir","echo rust"];
for ccc in &commands {
let rc = Command::new("cmd")
.arg("/C")
.arg(ccc)
.output()
.expect("there was an error");
io::stdout().write_all(&rc.stdout).unwrap();
// io::stdout().write_all(&rc.stderr).unwrap();
}
It seems like it runs the dir command within the source code's folder instead of C:\\Users\\MegaMind\\Documents\\dynamodb_local_latest. How can I get the cd to take effect?
The working directory applies on a per-process basis. Each Command is a separate process. The cd command changes the working directory for that process, and then that process exits and the record of that directory change is gone. Then you launch a new process (the one running dir) and it's still using the original working directory.
If you want to set the working directory for any given child command, just use the current_dir method when setting it up or (depending on what your needs are) you may want to change the Rust process's working directory (which will be inherited by child processes when not overridden).

Can I access CLI programs from within Node's child_process?

I’ve written a node script that cd’s into multiple directories in sequence and executes bash commands in order to deploy the contents of the repos to my development environment.
Native bash commands work fine, like cd, ls, but it looks like the subshell or child process (or whatever the proper term is, I don’t understand the inner workings of Bash) that’s opened by node doesn’t have anything available to my normal prompt.
E.g.
the custom bash toolset that’s available globally
nvm (is this even possible, to run a different version of node within a node subshell?)
gulp breaks because it doesn't have the necessary node version installed.
Is it possible to access these programs/commands from the node subshell? I’m using the child_process node module.
const { exec } = require('child_process');
function command (command) {
exec (command, (err, stdout, stderr) => {
if (err) {
error(err);
} else {
message(`stdout: ${stdout}`);
message(`stderr: ${stderr}`);
}
});
}
Used as in:
command('nvm use 6');
command('gulp build');
command('pde deploy');
The child process is not run as bash. child_process spawns the executable using the regular sh shell.
If you need the commands to run within bash, the command line you run needs to be wrapped in bash -c. For example:
command('bash -c "my command here"');
Also, each command you run is a sub-process, which does not affect the parent process, nor any subsequent sub processes. Thus, a shell built-in like cd will only change the directory for that sub-process, which immediately goes away. You will see this if you run:
command('cd /');
command('ls');
The ls command will show the current working directory, not the root directory.
If you run your command with bash -c and the $PATH and other environment variables still aren't set up the way you need them, you need to debug your shell start-up scripts. Perhaps there's a difference between interactive shells (.bash_profile) and all shells (.bashrc).
Note that fully non-interactive shells may need to explicitly have the start-up script you want to run specified.

How to access shell variable value from Node.js?

Let's say there is a variable key1 and its value is 123
key1=123
so when I run the command in linux environment echo $key1, I get output as 123.
Now I have the following gulp task.
const child_process = require('child_process');
....
gulp.task('printKeyValue', function() {
var value1 = child_process.execSync('echo $key1');
console.log(value1.toString().trim());
});
Here, I'm trying to access value of linux variable from nodejs by using Child Process
But when I run the following gulp task, I don't get the desired output.
npm run gulp -- printKeyValue
Instead I get output as $key1 and not 123.
See below screenshot
Other commands like ls & pwd in gulp task gives the desired output.
Can some one please help on this or suggest an alternate way?
You are not exporting the variable. When you just do
key1=123
the variable is not propagated to subprocesses. It will be available in your current bash process, so you can see it when you type echo $key1, but it will not get inherited by the node process. As man bash says:
When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following. Unless otherwise noted, the values are inherited from the shell.
[...]
shell variables and functions marked for export, along with variables exported for the command, passed in the environment
You need to either define the variable as exported
export key1=123
or mark an existing variable for export
key1=123
export key1
or launch your node with the modified environment, either via the bash innate capability to do so
key1=123 node code.js
or using /usr/bin/env utility:
env key1=123 node code.js
Once the variable is properly passed to the node process, it will be available both in process.env.key1 and as $key1 in a child process.
EDIT: I just noticed, you actually gave the command you're running; it does not matter, the same logic goes for every executable, whether node or npm or anything else.

Change current working directory of bash terminal with Nodejs

I want to develop a Node CLI app which clones a git repo and cd's into it.
For eg.
dummyuser:~$ dummy-cli-command dummy-git-repo-url
Cloning repository...
dummyuser:~/Workspace/dummy-git-repo$
I was able to clone it but am not able to cd into it. I know that node forks a child process under which it executes my code and there is no way to change directory of the bash terminal through which I first executed my node-cli-command. Can anyone help me with any workaround ?
You will not be able to do that - not with Node and not with any other program. You cannot even do it with a shell script, though you can do it with a shell alias or function but only if it is not run as a subprocess.
What it means is that you may be able to achieve that if your Node program returns the correct directory to a shell alias or shell function that runs it, and it's the alias or function that does the directory changing. That alias or function could know the destination in the first place if it gets it by command line arguments. But then again, if you use a shell function then you don't need Node to clone the repo in the first place.
The bottom line is that you will not be able to change the working directory of your shell from a subprocess so your only options are the ways to do it by running code in the same process like an alias or a function in the shell.

Why calling a script by "scriptName" doesn't work?

I have a simple script cmakeclean to clean cmake temp files:
#!/bin/bash -f
rm CMakeCache.txt
rm *.cmake
which I call like
$ cmakeclean
And it does remove CMakeCache.txt, but it doesn't remove cmake_install.cmake:
rm: *.cmake: No such file or directory
When I run it like:
$ . cmakeclean
it does remove both.
What is the difference and can I make this script work like an usual linux command (without . in front)?
P.S.
I am sure the both times is same script is executed. To check this I added echo meme in the script and rerun it in both ways.
Remove the -f from your #!/bin/bash -f line.
-f prevents pathname expansion, which means that *.cmake will not match anything. When you run your script as a script, it interprets the shebang line, and in effect runs /bin/bash -f scriptname. When you run it as . scriptname, the shebang is just seen as a comment line and ignored, so the fact that you do not have -f set in your current environment allows it to work as expected.
. script is short for source script which means the current shell executes the commands in the script. If there's an exit in there, the current shell will exit (and e. g. the terminal window will close).
This is typically used to modify the environment of the current shell (set variables etc.).
script asks the shell to fork itself, then exec the given script in the child process, and then wait in the father for the termination of the child. If there's an exit in the script, this will be executed by the child shell and thus only terminate this. The father shell stays intact and unaltered by this call.
This is typically used to start other programs from the current shell.
Is this about ClearCase? What did you do in your poor life where you've been assigned to work in the deepest bowels of hell?
For years, I was a senior ClearCase Administer. I haven't touched it in over a decade. My life is way better now. The sky is bluer, bird songs are more melodious, and my dread over coming to work every day is now a bit less.
Getting back to your issue: It's hard to say exactly what's going on. ClearCase does some wacky things. In a dynamic view, the ClearCase repository on Unix systems is hidden in the shell's environment. Now you see it, now you don't.
When you run a shell script, it starts up a new environment. If a particular shell variable is not imported, it is invisible that shell script. When you merely run cmakeclean from the command line, you are spawning a new shell -- one that does not contain your ClearCase environment.
When you run a shell script with a dot prefix like . cmakeclean, you are running that shell script in the current shell which contains your ClearCase environment. Thus, it can see your ClearCase view.
If you're using a snapshot view, it is possible that you have a $HOME/.bashrc that's changing directories on you. When a new shell environment runs in BASH (the default shell in MacOS X and Linux), it first runs $HOME/.bashrc. If this sets a particular directory, then you end up in that directory and not in the directory where you ran your shell script. I use to see this when I too was involved in ClearCase hell. People setup their .kshrc script (it was the days before BASH and most people used Kornshell) to setup their views. Unfortunately, this made running any other shell script almost impossible to do.

Resources