【问题标题】:How do I set the working directory of the parent process?如何设置父进程的工作目录?
【发布时间】:2011-01-23 10:02:03
【问题描述】:

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

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

附: shell 实用程序 chdir 成功地做到了完全相同,因此必须有一种编程方式来实现该效果。

【问题讨论】:

标签: linux gcc process parent working-directory


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    不要这样做。

    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 命令已缓存并且不会注意到。

    【讨论】:

    • 是否可以以相同的方式调用强制 Bash 检查(可能已修改)工作目录的过程?
    • @coderodde 查看 Bash 源代码,很明显 resetpwd("")chdir("..") 之后会处理缓存的密码。但是你没有读过“不要这样做”吗?说真的,不要。
    • 我同意“不要这样做”。从长远来看没有意义。
    • 这是一个攻击媒介。想象一个进程从它信任的目录中读取文件并执行它们,比如解释器。您可以更改进程的当前目录«unter dem Arsch»,并注入恶意代码。
    • @naply:此技术依赖于 ptrace 进程的能力,如果您可以 ptrace 进程,则已经没有安全边界。也称为“It rather involved being on the other side of this airtight hatchway”。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      alias cdfoo='cd theFooDir'
      

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

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

      【讨论】:

        【解决方案5】:

        I solved this 的方式是拥有一个调用脚本并获取脚本编写的文件的 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
        

        这仍然是一件坏事。

        【讨论】:

        • 我觉得这是真正的答案。如果您正在编写 CLI 并且希望它能够 cd 左右,那么这是要走的路。
        【解决方案6】:

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

        不可能使用任何“可接受”的方式。可以接受,我的意思是“没有粗暴地入侵你的系统(例如使用 gdb)”;)

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

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


        附带说明,这就是为什么cd ("chdir") 是一个internal shell 命令,而不是一个external 实用程序。如果是这样,它将无法更改 shell 的工作目录。

        【讨论】:

          【解决方案7】:

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

          感谢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 <dirname> 插入其中。在 Midnight Commander 的情况下,它还插入了两个 Ctrl+O(面板开/关)键盘命令,并且以一种非常骇人听闻的方式,使用 sleep 进行进程间同步(这很遗憾,但它确实有效)。

          /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,但它不适用于午夜指挥官:

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

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

          【讨论】:

            【解决方案8】:

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

            不过,类似的替代品很少:

            1. 启动一个子shell并在那里更改目录(不会影响父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。这是一种作弊,因为命令是在您完成进程后执行的,而您有点强迫用户执行某些命令。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多