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).
Related
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.
When beginning to work, I have to run several commands:
source work/tools
cd work/tool
source tool
setup_tool
Off course, doing this a few times a day is really annonying, so I tried to make a bash script tool where I put these commands and put it in /user/bin to run it with command
tool
However, there is a problem. When i run the script and then try to work by typing some of the tool-based commands, it does not work.
I figured out, that it is fine, since if I make a script and then run it, the script seems to run in the same terminal window, but what it really does is, that it behaves as if it created a "hidden window" for its execution and after termination of the script, the "hidden window" terminates too. So I am asking - is there a way to automatize the source command?
I have tried using xterm -hold -e command, but it runs the programmed script in the new window. Obviously, I don't want that. How can I achieve running it in the current window?
Don't put files like that in /usr/bin. As a general rule you don't want to mess with the distribution owned locations like that. You can use /usr/local/bin if you need a system-wide location or you can create a directory in your home directory to hold things like this that are for your own usage (and add that to the $PATH).
What you've noticed is that when run as a script on its own (tool, /path/to/tool, etc.) that the script runs in its own shell session (nothing to do with terminal windows as-such) and you don't want that (as the changes the script makes don't persist to your current shell session).
What you want to do instead is "source"/run the script in your current session. Which you are already doing with that set of commands you listed (source work/tools is doing exactly that).
So instead of running tool or /path/to/tool instead use source /path/to/tool or . /path/to/tool.
As fedorqui correctly points out you don't even need a script for this anywhere as you can just make a shell function for this instead (in your normal shell startup files .bashrc, etc.) and then just run that function when you need to so that setup.
Be careful to use full paths for things when you do this though since you, presumably, want this to work no matter what directory you happen to be in when you run it.
It doesn't create a new hidden window, nor does it create a terminal. What happens is that if you're running a script, normally it runs on a new shell process. The script you're running is supposed to modify the shell environment, but if you're running the script in a new shell process, that shell process's environment is the one that gets modified, instead of your shell environment.
Scripts that needs to modify the current shell environments usually must be run with the source command. What you need to do is to run the script in the current shell. So you should do source /path/to/tool.
If you want to be able to source the script with just tool, put this in your alias file/shell startup (check your distro doc where the file is, but it's usually either .bash_aliases or .bashrc):
alias tool="source /path/to/tool"
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.
While writing a perl script intended to fully automate the setup of virtual machines (Xen pv) I hit a small maybe very simple problem.
Using perl's chroot function I do my things on the guest file system and then I need to get back to my initial real root. How the hell I do that?
Script example:
`mount $disk_image $mount_point`;
chdir($mount_point);
chroot($mount_point);
#[Do my things...]
#<Exit chroot wanted here>
`umount $mount_point`;
#[Post install things...]
I've tried exit; but obviously that exit the whole script.
Searching for a way to exit the chroot I've found a number of scripts who aim to exit an already setup chroot (privilege escalation). Since I do the chroot here theses methods do not aplies.
Tried some crazy things like:
opendir REAL_ROOT, "/";
chdir($mount_point);
chroot($mount_point);
chdir(*REAL_ROOT);
But no go.
UPDATE
Some points to consider:
I can't split the script in multiple files. (Silly reasons, but really, I can't)
The chrooted part involve using a lot of data gathered earlier by the script (before the chroot), enforcing the need of not lunching another script inside the chroot.
Using open, system or backticks is not good, I need to run commands and based on the output (not the exit code, the actual output) do other things.
Steps after the chroot depends on what was done inside the chroot, hence I need to have all the variables I defined or changed while inside, outside.
Fork is possible, but I don't know a good way to handle correctly the passing of informations from and to the child.
The chrooted process() cannot "unchroot" itself by exiting (which would just exit).
You have to spawn a children process, which will chroot.
Something along the lines of the following should do the trick:
if (fork())
{
# parent
wait;
}
else
{
# children
chroot("/path/to/somewhere/");
# do some Perl stuff inside the chroot...
exit;
}
# The parent can continue it's stuff after his chrooted children did some others stuff...
It stills lacks of some error checking thought.
You can't undo a chroot() on a process - that's the whole point of the system call.
You need a second process (a child process) to do the work in the chrooted environment. Fork, and have the child undergo the chroot and do its stuff and exit, leaving the parent to do the cleanup.
Try spawning a child process that does the chroot, e.g. with system or fork depending on your needs, and waiting for the child to return the main program continues.
This looks like it might be promising:
Breaking Out of a Chroot Jail Using PERL
Save the original root as the current working directory or as a file descriptor:
chdir "/";
chroot "/mnt";
# Do something
chroot ".";
OR
open DIR, "<", "/";
chroot "/mnt";
# Do something
chdir DIR;
chroot ".";
close DIR;
I have been trying to figure out how a shell knows which directory you're currently in. I know there is an environment variable $PWD but when I try changing it manually, it changes what my shell shows at the prompt but commands like ls and cd are unaffected.
cd is an internal shell command so I can understand it might use info stored within the shell memory, but ls is external and yet running ls without anything will give me whatever directory I was originally in regardless what I do to $PWD.
Each process has its own individual current working directory which the Linux system tracks. This is one of the pieces of information the OS manages for each process. There is a system call getcwd() which retrieves this directory.
The $PWD environment variable reflects what getcwd() was the last time the shell checked, but changing it does not actually change the current directory. To do that the shell would have to call chdir() when $PWD changes, which it does not do.
This also is the reason cd has to be a shell built-in. When you run a sub-process that child process gets its own working directory, so if cd were an executable then its calls to chdir() would be useless as that would not change its parent's working directory. It would only be changing its own (short-lived) working directory. Hence, cd is a shell built-in to avoid a sub-process being launched.
The shell sets that variable, but stores the knowledge internally (which is why you can't make cd an external program, it must be a built-in). The shell prompt is composed just before it is displayed each time, and you have specified using $PWD in yours, so the shell reads that in.
Remember: the shell is just a program, like any other program. It can---and does---store things in variables.
As AndiDog and John point out unix-like systems (i.e. including linux) actually maintains the working directory for each process through a set of system calls. The storage is still process local, however.
The Linux kernel stores the current directory of each process. You can look it up in the /proc filesystem (for example, "/proc/1/cwd" for the init process).
The current directory can be changed with the chdir syscall and retrieved with getcwd.
The current directory is a property of a running program (process) that gets inherited by processes created by that process. Changing the current directory is made via an operating system call. The shell maps the cd operation to that system call. When you write an external program like ls, that program inherits the current directory.
The $PWD variable is how the shell shows you the current directory for you to use it as a variable if you need it. Changing it does not have effect in the real current directory of the shell itself.
You (OP) launch ls via your command shell, and any process you launch, the shell launches in the context of its current working directory. So, each process you launch has its own $PWD variable (in a way).