user285728
user285728

Reputation: 293

How do I set the working directory of the parent process?

As the title reveals it, we are writing a Unix-style shell utility U that is supposed to be invoked (in most cases) from bash.

How exactly could U change the working directory of bash (or parent in general)?

P.S. The shell utility chdir succeeds in doing exactly the same, thus there must be a programmatic way of achieving the effect.

Upvotes: 29

Views: 15102

Answers (8)

Tombart
Tombart

Reputation: 32446

You can't. Like in real life you can't change the path of your parents :)

Though, there are few similar alternatives:

  1. Start a subshell and change directory there (won't affect the parent).
  2. Attach to a certain console /dev/ttyX where X is usually S0 - S63 and execute commands there.
  3. Send a message to a parent process with the command you want to execute:
#include <sys/ioctl.h>

void inject_shell(const char* cmd){
  int i = 0;
  while (cmd[i] != '\0'){
    ioctl(0, TIOCSTI, &cmd[i++]);
  }
}

int main(void){
  inject_shell("cd /var\r");
  return 0;
}

compile and run it:

$ gcc inject.c -o inject
$ ./inject
cd /var
/var $

When the string is terminated with \r it might imitate hitting the Enter key (carriage return) - depending on your shell this could work or try \r\n. This is a cheating since the command is executed after finishing your process and you're kind of forcing the user to execute some command.

Upvotes: 2

18446744073709551615
18446744073709551615

Reputation: 16852

I am not sure if it is a "don't do this" too...

Thanks to the extremely useful discussion in https://unix.stackexchange.com/questions/213799/can-bash-write-to-its-own-input-stream/ ...

The tailcd utility (for "tail-call cd") that works both in bash and under the Midnight Commander allows usage in scripts like

/bin/mkcd:

mkdir "$1" && tailcd "$1"

The implementation is tricky and it requires xdotool. The tailcd command must be the last command in the script (this is a typical compatibility requirement for utilities that allow multiple implementations). It hacks the bash input stream, namely, inserts cd <dirname> into it. In the case of Midnight Commander, it in addition inserts two Ctrl+O (panels on/off) keyboard commands and, in a very hackish manner, uses sleep for inter-process synchronization (which is a shame, but it works).

/bin/tailcd:

#! /bin/bash
escapedname=`sed 's/[^a-zA-Z\d._/-]/\\\\&/g' <<< "$1"`
if [ -z "$MC_TMPDIR" ] ; then
xdotool type " cd $escapedname  "; xdotool key space Return
else
(sleep 0.1; xdotool type " cd $escapedname "; xdotool key space Return Ctrl+o; sleep 0.1; xdotool key Ctrl+o )&
fi

(The space before cd prevents the inserted command from going to the history; the spaces after the directory name are required for it to work but I do not know why.)

Another implementation of tailcd does not use xdotool, but it does not work with Midnight Commander:

#!/bin/bash
escapedname=`sed 's/[^a-zA-Z\d._/-]/\\\\&/g' <<< "$1"`
perl -e 'ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV' " cd" "$escapedname" $'\r'

Ideally, tailcd would/should be a part of bash, use normal inter-process communication, etc.

Upvotes: 0

Sylvain Leroux
Sylvain Leroux

Reputation: 52040

How exactly could you change the working directory of bash (or parent in general)?

It is not possible by using any "acceptable" way. By acceptable, I mean "without outrageously hacking your system (using gdb for example)" ;)

More seriously, when the user launch an executable, the child process will run in its own environment, which is mostly a copy of its parent environment. This environment contains "environment variables" as well as the "current working directory", just to name those two.

Of course, a process can alter its own environment. For example to change its working directory (like when you cd xxx in you shell). But since this environment is a copy, that does not alter the parent's environment in any way. And there is no standard way to modify your parent environment.


As a side note, this is why cd ("chdir") is an internal shell command, and not an external utility. If that was the case, it wouldn’t be able to change the shell's working directory.

Upvotes: 5

Xiong Chiamiov
Xiong Chiamiov

Reputation: 13744

The way I solved this is to have a shell alias that calls the script and source a file that the script wrote. So, for instance,

function waypoint {
    python "$WAYPOINT_DIRECTORY"/waypoint.py $@ &&
    source ~/.config/waypoint/scratch.sh
    cat /dev/null > ~/.config/waypoint/scratch.sh
}

and waypoint.py creates scratch.sh to look like

cd /some/directory

This is still a Bad Thing.

Upvotes: 5

user811773
user811773

Reputation:

In case you are running the shell interactively and the target directory is static, you can simply put an alias into your ~/.bashrc file:

alias cdfoo='cd theFooDir'

When dealing with non-interactive shell scripts, you can create a protocol between the parental Bash script and the child Bash script. One method of how to implement this is to let the child script save the path into a file (such as ~/.new-work-dir). After the child process terminates, the parental process will need to read this file (such as cd `cat ~/.new-work-dir`).

If you plan to use the rule mentioned in the previous paragraph very often, I would suggest you download Bash source code and patch it so that it automatically changes the working directory to the contents of ~/.new-work-dir after each time it runs a command. In the patch, you could even implement an entirely new Bash built-in command which suits your needs and implements the protocol you want it to implement (this new command probably won't be accepted by the Bash maintainers). But, patching works for personal use and for use in a smaller community.

Upvotes: 1

JustJeff
JustJeff

Reputation: 12980

The chdir command is a shell built-in, so it has direct access to the working directory of the shell that executes it. Shells are usually pretty good at protecting themselves from the effects of scripts, giving the child process a copy of the shell's own working environment. When the child process exits, the environment it used is deleted.

One thing you can do is 'source' a script. This lets you change the directory because in essence, you are telling the shell to execute the commands from the file as though you had typed them in directly. I.e., you're not working from a copy of the shell's environment, you are working directly on it, when sourcing.

Upvotes: 5

Vlad
Vlad

Reputation: 35594

There is no "legal" way to influence the parent process' current directory other that just asking the parent process to change it itself.

chdir which changes the directory in bash scripts is not an external utility, it's a builtin command.

Upvotes: 7

ephemient
ephemient

Reputation: 204926

Don't do this.

FILE *p;
char cmd[32];
p = fopen("/tmp/gdb_cmds", "w");
fprintf(p, "call chdir(\"..\")\ndetach\nquit\n");
fclose(p);
sprintf(cmd, "gdb -p %d -batch -x /tmp/gdb_cmds", getppid());
system(cmd);

It will probably work, though note that Bash's pwd command is cached and won't notice.

Upvotes: 35

Related Questions