如何设置父进程的工作目录?

发布于 2024-08-23 16:04:41 字数 219 浏览 6 评论 0原文

正如标题所示,我们正在编写一个 Unix 风格的 shell 实用程序 U,它应该从 bash 调用(在大多数情况下)。

U 究竟如何更改 bash 的工作目录(或一般的父目录)?

PS shell 实用程序 chdir 成功地完成了完全相同的操作,因此必须有一种编程方式来实现该效果。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

肩上的翅膀 2024-08-30 16:04:41

不要这样做。

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);

可能会工作,但请注意,Bash 的 pwd 命令已缓存并且不会注意到。

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.

小红帽 2024-08-30 16:04:41

除了要求父进程自行更改之外,没有“合法”方法可以影响父进程的当前目录。

更改 bash 脚本中的目录的 chdir 不是外部实用程序,而是内置命令。

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.

吹梦到西洲 2024-08-30 16:04:41

chdir 命令是 shell 内置命令,因此它可以直接访问执行它的 shell 的工作目录。 shell 通常非常擅长保护自己免受脚本的影响,为子进程提供 shell 自身工作环境的副本。当子进程退出时,它使用的环境将被删除。

您可以做的一件事就是“获取”脚本。这允许您更改目录,因为本质上,您是在告诉 shell 执行文件中的命令,就像您直接键入它们一样。也就是说,您不是在 shell 环境的副本中工作,而是在采购时直接在其上工作。

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.

是你 2024-08-30 16:04:41

我解决这个问题的方法是使用一个 shell 别名来调用脚本并获取脚本编写的文件。例如,

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

waypoint.py 创建 scratch.sh 看起来

cd /some/directory

这仍然是一件坏事。

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.

夜未央樱花落 2024-08-30 16:04:41

究竟如何更改 bash 的工作目录(或一般情况下的父目录)?

使用任何“可接受”的方式不可能。我所说的可接受,是指“无需粗暴地攻击您的系统(例如使用gdb)”;)

更严重的是,当用户启动可执行文件时,子进程将在其自己的运行 em> 环境,主要是其父环境的副本。该环境包含“环境变量”以及“当前工作目录”,仅举两个例子。

当然,进程可以改变它自己的环境。例如,更改其工作目录(例如在 shell 中 cd xxx 时)。但由于这个环境是一个副本,因此不会以任何方式改变父环境。并且没有标准的方法来修改您的父环境。


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.

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.

口干舌燥 2024-08-30 16:04:41

你不能。就像在现实生活中一样,您无法更改父母的路径:)

不过,有一些类似的替代方案:

  1. 启动一个子 shell 并更改那里的目录(不会影响父级)。
  2. 连接到某个控制台 /dev/ttyX,其中 X 通常是 S0 - S63 并在那里执行命令。
  3. 使用您要执行的命令向父进程发送消息:
#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;
}

编译并运行它:

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

当字符串以 \r 终止时,它可能会模仿按 Enter 键(回车) - 根据您的 shell,这可能有效或尝试 \r\n。这是一种作弊行为,因为该命令是在完成进程后执行的,并且您有点强迫用户执行某些命令。

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.

愁杀 2024-08-30 16:04:41

如果您以交互方式运行 shell 并且目标目录是静态的,您可以简单地将别名放入 ~/.bashrc 文件中:

alias cdfoo='cd theFooDir'

在处理非交互式 shell 脚本时,您可以创建一个协议父 Bash 脚本和子 Bash 脚本之间。实现此目的的一种方法是让子脚本将路径保存到文件中(例如 ~/.new-work-dir)。子进程终止后,父进程需要读取此文件(例如 cd `cat ~/.new-work-dir` )。

如果你打算经常使用上一段提到的规则,我建议你下载 Bash 源代码并修补它,以便它自动将工作目录更改为 ~/.new-work-dir< 的内容/code> 每次运行命令后。在补丁中,您甚至可以实现一个全新的 Bash 内置命令,它适合您的需求并实现您希望它实现的协议(这个新命令可能不会被 Bash 维护者接受)。但是,修补适用于个人使用和较小的社区。

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.

只有一腔孤勇 2024-08-30 16:04:41

我不确定这是否也是“不要这样做”...

感谢 https://unix.stackexchange.com/questions/213799/can-bash-write-to-its-own-input-stream/ ...

tailcd 实用程序(用于“tail-call cd”)可在 bash 和 Midnight Commander 下运行,允许在

/bin 等脚本中使用/mkcd:

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

实现很棘手,需要xdotooltailcd 命令必须是脚本中的最后一个命令(这是允许多种实现的实用程序的典型兼容性要求)。它侵入 bash 输入流,即将 cd插入其中。就 Midnight Commander 而言,它还插入了两个 Ctrl+O(面板开/关)键盘命令,并以一种非常黑客的方式使用睡眠来进行进程间同步(这很遗憾,但它确实有效)。

/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

cd 之前的空格阻止插入的命令进入历史记录;目录名后面的空格需要它才能工作,但我不知道为什么。)

另一个实现tailcd 不使用 xdotool,但它不适用于 Midnight Commander:

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

理想情况下,tailcd 将/应该是 bash 的一部分,使用普通进程间通信等

\r'

理想情况下,tailcd 将/应该是 bash 的一部分,使用普通进程间通信等

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" 

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

\r'

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文