问题
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.
回答1:
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.
回答2:
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.
回答3:
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.
回答4:
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.
回答5:
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.回答6:
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.
回答7:
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.
来源:https://stackoverflow.com/questions/2375003/how-do-i-set-the-working-directory-of-the-parent-process