正如标题所示,我们正在编写一个类Unix风格的shell实用程序U,它通常应该从bash中调用。
那么U如何精确地改变bash(或者一般情况下的父进程)的工作目录呢?
P.S. shell实用程序chdir可以成功地做到同样的效果,因此必须有一种程序化的方法来实现该效果。
正如标题所示,我们正在编写一个类Unix风格的shell实用程序U,它通常应该从bash中调用。
那么U如何精确地改变bash(或者一般情况下的父进程)的工作目录呢?
P.S. shell实用程序chdir可以成功地做到同样的效果,因此必须有一种程序化的方法来实现该效果。
不要这样做。
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
命令会被缓存,不会意识到变化。
chdir("..")
之后使用resetpwd("")
可以处理缓存的pwd。但是你没有读到“不要这样做”吗?认真地,不要这样做。 - ephemientpwd
感到困惑,而且cd
也可以在说“没有这样的文件或目录”时进入一个文件夹(我用ls
检查了自己的位置)。 - Scz除了要求父进程自己更改当前目录,没有其他“合法”的方式可以影响父进程的当前目录。
chdir
用于更改bash脚本中的目录,它不是外部工具,而是一个内置命令。
cd
(“chdir”)是一个内部 shell命令,而不是一个外部实用程序。如果是这样,它就无法更改shell的工作目录。
我解决这个问题的方法是创建一个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
这仍然是一件坏事。
cd
,那么这是正确的方法。 - Gustavo Maciel/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
。这是一种欺骗行为,因为该命令在完成进程后执行,并且您有点强制用户执行某些命令。
如果您正在交互式地运行shell,并且目标目录是静态的,您可以将别名放入您的~/.bashrc
文件中:
alias cdfoo='cd theFooDir'
在处理非交互式shell脚本时,您可以在父Bash脚本和子Bash脚本之间创建协议。如何实现这一点的一种方法是让子脚本将路径保存到文件中(例如~/.new-work-dir
)。在子进程终止后,父进程需要读取此文件(例如cd `cat ~/.new-work-dir`
)。
如果您计划经常使用上述段落中提到的规则,我建议您下载Bash源代码并打补丁,以便在每次运行命令后自动将工作目录更改为~/.new-work-dir
的内容。在补丁中,您甚至可以实现一个全新的Bash内置命令,以满足您的需求并实现您想要实现的协议(这个新命令可能不会被Bash维护者接受)。但是,打补丁适用于个人使用和较小社区的使用。
我不确定这是否也是一个“不要这样做”的例子...
非常感谢在https://unix.stackexchange.com/questions/213799/can-bash-write-to-its-own-input-stream/中的极其有用的讨论...
tailcd
实用程序(用于“尾调用 cd
”),既可以在 bash 中,也可以在 Midnight Commander 下使用,可在脚本中使用,例如:
/bin/mkcd:
mkdir "$1" && tailcd "$1"
xdotool
。 tailcd
命令必须是脚本中的最后一个命令(这是允许多个实现的实用程序的典型兼容性要求)。 它会黑掉bash输入流,即将cd <dirname>
插入其中。 对于Midnight Commander,它还会插入两个Ctrl + O(面板开/关)键盘命令,并以非常hackish的方式使用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 的一部分,使用正常的进程间通信等。