27

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

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

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

4

8 回答 8

35

不要这样做。

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

于 2010-03-03T21:36:52.380 回答
7

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

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

于 2010-03-03T21:16:23.167 回答
5

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

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

于 2010-03-03T23:51:09.170 回答
5

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

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

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

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


作为旁注,这就是为什么cd("chdir") 是一个内部shell 命令,而不是一个外部实用程序。如果是这种情况,它将无法更改 shell 的工作目录。

于 2013-08-11T11:02:42.660 回答
4

我解决这个问题的方法是有一个 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

这仍然是一件坏事。

于 2012-02-17T19:54:51.983 回答
0

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

alias cdfoo='cd theFooDir'

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

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

于 2011-10-12T12:36:49.603 回答
0

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

感谢https://unix.stackexchange.com/questions/213799/can-bash-write-to-its-own-input-stream/中非常有用的讨论...

在 bash 和 Midnight Commander 下都可以使用的tailcd实用程序(用于“tail-call cd”)允许在脚本中使用,例如

/bin/mkcd:

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

实现很棘手,它需要xdotool. tailcd命令必须是脚本中的最后一个命令(这是允许多个实现的实用程序的典型兼容性要求)。它破解 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,但它不适用于 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'

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

于 2018-08-23T12:23:19.417 回答
0

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

但是,几乎没有类似的选择:

  1. 启动一个子shell并在那里更改目录(不会影响父级)。
  2. 连接到通常/dev/ttyX所在的某个控制台-并在那里执行命令。XS0S63
  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击键(回车) - 根据您的外壳,这可以工作或尝试\r\n。这是一种作弊,因为该命令是在完成您的进程后执行的,并且您有点强迫用户执行某些命令。

于 2020-12-22T09:30:48.743 回答