Как мне установить рабочий каталог родительского процесса?
Как видно из названия, мы пишем утилиту оболочки в стиле Unix U, которая должна вызываться (в большинстве случаев) из bash.
Как именно U может изменить рабочий каталог Bash (или родительский в целом)?
PS Утилита оболочки chdir делает то же самое, поэтому должен быть программный способ достижения эффекта.
8 ответов
Не делай этого.
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);
Это вероятно будет работать, хотя обратите внимание, что Баш pwd
команда кешируется и не заметит.
Не существует "легального" способа повлиять на текущий каталог родительского процесса, кроме как просто попросить родительский процесс изменить его сам.
chdir
который изменяет каталог в скриптах bash, это не внешняя утилита, а встроенная команда.
Команда chdir является встроенной в оболочку, поэтому она имеет прямой доступ к рабочему каталогу оболочки, которая ее выполняет. Оболочки обычно довольно хорошо защищают себя от воздействия сценариев, предоставляя дочернему процессу копию собственной рабочей среды оболочки. Когда дочерний процесс завершается, среда, которую он использовал, удаляется.
Одна вещь, которую вы можете сделать, это "поставить" сценарий. Это позволяет вам изменить каталог, потому что по сути вы говорите оболочке выполнять команды из файла, как если бы вы вводили их напрямую. То есть, вы не работаете с копией среды оболочки, вы работаете непосредственно с ней, при поиске источников.
Как именно вы можете изменить рабочий каталог Bash (или родительский в целом)?
Это невозможно, используя любой "приемлемый" способ. Под приемлемым я подразумеваю "без возмутительного взлома вашей системы (например, используя gdb)";)
Более серьезно, когда пользователь запускает исполняемый файл, дочерний процесс будет запускаться в своей собственной среде, которая в основном является копией его родительской среды. Эта среда содержит "переменные среды", а также "текущий рабочий каталог", просто чтобы назвать эти две.
Конечно, процесс может изменить свою среду. Например, чтобы изменить его рабочий каталог (например, когда вы cd xxx
в твоей скорлупе). Но поскольку эта среда является копией, это никак не влияет на среду родителя. И нет никакого стандартного способа изменить вашу родительскую среду.
Как примечание стороны, вот почему
cd
("chdir") - это внутренняя команда оболочки, а не внешняя утилита. Если бы это было так, он не смог бы изменить рабочий каталог оболочки.
Я решил это, имея псевдоним оболочки, который вызывает сценарий, и создает файл, который написал сценарий. Так, например,
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
Это все еще плохая вещь.
Вы не можете. Как и в реальной жизни, вы не можете изменить путь своих родителей :)
Хотя похожих альтернатив немного:
- Запустите подоболочку и измените там каталог (не повлияет на родителя).
- Привязать к определенной консоли
/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клавиши (возврат каретки) - в зависимости от вашей оболочки это может сработать или попробовать
\r\n
. Это мошенничество, поскольку команда выполняется после завершения вашего процесса, и вы как бы вынуждаете пользователя выполнить какую-то команду.
Если вы используете оболочку в интерактивном режиме, а целевой каталог статический, вы можете просто поместить псевдоним в свой ~/.bashrc
файл:
alias cdfoo='cd theFooDir'
При работе с неинтерактивными сценариями оболочки вы можете создать протокол между родительским сценарием 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, позволяет использовать его в таких скриптах, как
/ Бен / mkcd:
mkdir "$1" && tailcd "$1"
Реализация хитрая и требует xdotool
, tailcd
команда должна быть последней командой в скрипте (это типичное требование совместимости для утилит, которые допускают несколько реализаций). Он взламывает поток ввода bash, а именно вставки cd <dirname>
внутрь. В случае Midnight Commander, он дополнительно вставляет две команды клавиатуры Ctrl+O (включение / выключение панелей) и очень хакерским способом использует sleep для межпроцессной синхронизации (что обидно, но работает).
/ Бен / 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, использовать нормальное межпроцессное взаимодействие и т. д.