正如标题所揭示的那样,我们正在编写一个Unix风格的shell实用程序 U ,它应该从bash中调用(在大多数情况下)。
U 究竟如何更改bash(或一般的父级)的工作目录?
P.S。 shell实用程序 chdir 成功完成相同的操作,因此必须采用编程方式来实现此效果。
答案 0 :(得分:32)
不要这样做。
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
命令已缓存,不会注意到。
答案 1 :(得分:6)
没有“合法”的方式来影响父进程的当前目录,只是要求父进程自己更改它。
更改bash脚本中目录的 chdir
不是外部实用程序,它是内置命令。
答案 2 :(得分:3)
chdir命令是内置的shell,因此它可以直接访问执行它的shell的工作目录。 Shell通常非常善于保护自己免受脚本的影响,使子进程能够处理shell自身工作环境的副本。当子进程退出时,它所使用的环境将被删除。
你可以做的一件事是'源'脚本。这允许您更改目录,因为实质上,您告诉shell执行文件中的命令,就像您直接键入它们一样。也就是说,你不是在shell环境的副本上工作,而是在采购时直接使用它。
答案 3 :(得分:3)
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
这仍然是一件坏事。
答案 4 :(得分:3)
您究竟如何更改bash(或父)的工作目录?
使用任何“可接受”的方式不。可接受的,我的意思是“没有肆无忌惮地攻击你的系统(例如使用 gdb )”;)
更严重的是,当用户启动可执行文件时,子进程将在其自己的环境中运行,该环境主要是其父环境的副本。这个环境包含“环境变量”以及“当前工作目录”,只是为了命名这两个。
当然,进程可以改变其自己的环境。例如,更改其工作目录(例如,在shell中cd xxx
时)。但由于此环境是副本,因此不会以任何方式改变父级环境。并且没有标准的方法来修改您的父环境。
cd
(“chdir”)是内部 shell命令而不是外部实用程序的原因。如果是这种情况,它将无法更改shell的工作目录。
答案 5 :(得分:0)
如果您以交互方式运行shell并且目标目录是静态的,则只需将别名放入~/.bashrc
文件中:
alias cdfoo='cd theFooDir'
在处理非交互式shell脚本时,您可以在父级Bash脚本和子Bash脚本之间创建协议。如何实现这一点的一种方法是让子脚本将路径保存到文件中(例如~/.new-work-dir
)。子进程终止后,父进程将需要读取此文件(例如cd `cat ~/.new-work-dir`
)。
如果您打算经常使用上一段中提到的规则,我建议您下载Bash源代码并对其进行修补,以便在每次执行后自动将工作目录更改为~/.new-work-dir
的内容运行一个命令。在补丁中,您甚至可以实现一个全新的Bash内置命令,该命令适合您的需要并实现您希望它实现的协议(这个新命令可能不会被Bash维护者接受)。但是,修补工具适合个人使用,也适用于较小的社区。 p>
答案 6 :(得分: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用于进程间同步(这很可惜,但是可以)。 / p>
/ 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的一部分,使用正常的进程间通信等。
答案 7 :(得分:0)
你不能。就像在现实生活中一样,您无法改变父母的道路:)
不过,几乎没有类似的替代方案:
/dev/ttyX
,其中 X
通常是 S0
- S63
并在那里执行命令。#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
。这是一种作弊,因为该命令是在完成您的进程后执行的,您有点强迫用户执行某些命令。