Вопрос или проблема
можно ли убить/выйти из текущего bash
шелла и запустить новый с помощью какой-то команды? Такое как
kill -9 $PPID && bash -c echo 'I started new!'
что, очевидно, не работает.
Конечно. Как только вы входите в систему, введите “bash”, чтобы открыть новый шелл поверх вашего логина.
Затем делайте все, что вы хотите, в той шелл-среде.
Когда вы будете готовы убить это и начать “с чистого листа” в новом шелле, просто введите “exit”, а потом снова “bash”.
Вы не убиваете шеллы, вы убиваете процессы. Вы можете запускать процессы и команды, включая шеллы.
Запуск процесса осуществляется клонированием (форканием) его.
Запуск команды будет означать ее выполнение.
Что-то обычно запускает скрипт шелла (например, bash) путем форкания себя, и в одной из ветвей форка (обычно в дочернем процессе) выполняется execve("/path/to/bash", ["bash", "/path/to/the-script"], environ)
.
Этот процесс будет читать содержимое the-script
по одной строке за раз и интерпретировать код шелла в нем.
Перед этим он инициализирует несколько специальных параметров, включая $$
для pid процесса, который его выполнил, $PPID
для родительского процесса (на тот момент; это может быть другая ветвь вышеуказанного форка или pid 1 или дочерний subreaper).
Некоторые из прочитанного кода часто заставляют процесс шелла порождать больше процессов (форкануть себя), в некоторых из которых он будет выполнять больше команд. Ведь шелл — это интерпретатор языка, специализированный на выполнение команд, и их нужно запускать в отдельных процессах, если вы хотите выполнить более одной команды.
Для тех команд, которые являются встроенными в шелл, такими как kill
, шеллу не нужно делать форк для их выполнения.
Стоит отметить, что в этих дочерних процессах $$
и $PPID
остаются неизменными, независимо от того, все ли еще соответствующие процессы живы или нет. Это не pid и ppid шелла, который, случайно, расширяет эти параметры, это pid и ppid процесса, который изначально запустил интерпретатор шелла.
Так что, когда вы делаете kill -9 $PPID
, это (при условии, что $IFS
не был изменен, так как вы забыли заключить это расширение в кавычки) выполнит команду kill
в том же процессе, который может быть или не быть тем, который изначально запустил шелл (если этот код найден в subshell или pipeline, например), и отправит сигнал SIGKILL (сигнал, который нельзя обработать и должен использоваться только как крайняя мера) процессу-родителю, от процесса, который запустил шелл в момент его инициализации.
Это не убьет шелл, хотя шелл может умереть как следствие смерти своего родителя. Например, если родитель — это эмулятор терминала, процесс(ы) шелла могут получить SIGHUP, когда терминал закроется.
Если вы замените это на kill "$$"
, тогда процесс, который изначально запустил шелл как дочерний процесс и который может все еще ожидать завершения этого процесса, увидит, что тот умер, и продолжит свои дела.
Если это процесс с pid равным $$
, который выполнил kill "$$"
, тогда он умрет и не сможет продолжить bash -c echo 'I started new!'
.
Что вы можете сделать здесь, так это:
exec bash -c echo 'I started new!'
exec
скорее нужно было бы назвать no-fork
. Это говорит текущему процессу шелла выполнить bash
в том же процессе, а не в дочернем, так что, по сути, это перезапустит шелл, переисполнит его в том же процессе. Этот процесс не завершится. Если у него все еще есть родитель, ожидающий его, то этот родитель не увидит его конец, пока новый шелл bash не вызовет exit
или не будет убит.
Вы также можете сделать:
bash -c echo 'I started new!' & exit
В этом случае текущий процесс шелла запускает bash
в качестве своего дочернего процесса, но не ждет его завершения и выходит сразу.
Затем, если у него есть родитель, он увидит, что процесс шелла завершается, но не будет знать о новом процессе bash (своем внуке).
Ответ или решение
Вопрос о том, как завершить текущий bash-скрипт и запустить новый с заданной командой, требует глубокого понимания работы процессов и механизмов запуска команд в операционных системах на базе UNIX. В частности, он подразумевает правильное использование команд оболочки bash и возможных сценариев работы, таких как exec
, fork
и управление процессами. Рассмотрим это поэтапно, уделяя внимание теоретической основе, примерам и практическому применению.
Теория
В операционных системах типа UNIX, включая Linux, процессы представляют собой исполняемые единицы, которые создаются и контролируются ядром системы. Запуск нового процесса обычно осуществляется через системный вызов fork()
, который создает копию текущего процесса, и execve()
, который загружает в процесс новую программу. В случае с оболочкой bash, процессы используются для запуска командных интерпретаторов, скриптов и различных задач.
Специфическим элементом работы в bash является использование переменных среды $$
и $PPID
. Переменная $$
обозначает ID текущего процесса оболочки (PID), а переменная $PPID
– ID родительского процесса. Эти переменные и их значения критично важны при управлении процессами, так как именно через них идентифицируется и управляется текущая и родительская сессии.
Пример
Допустим, у вас есть задача завершить текущий сеанс оболочки и сразу запустить новый с выполнением конкретной команды. Неправильное использование kill
по отношению к текущему процессу вызовет ситуацию, в которой текущая оболочка завершится прежде, чем новая будет запущена, нарушая ожидаемую последовательность.
Корректным решением может быть использование команды exec
, которая позволяет заменить текущий процесс новым, не создавая дополнительных потомков. Рассмотрим использование команды exec
:
exec bash -c "echo 'I started new!'"
Здесь exec
запускает новую команду в текущем процессе вместо создания дочернего. Это значит, что текущий bash закончит своё выполнение, и вместо него начнёт выполняться bash -c "echo 'I started new!'"
, что и соответствует вашей задаче.
Применение
На практике это полезно в скриптах и сценариях автоматизации. Например, если скрипт требует "перезапуска" в тех же условиях, включая наследование всех файловых дескрипторов и переменных окружения, использование exec
даёт возможность сделать это безопасно и согласованно.
Ещё один сценарий:
bash -c "echo 'I started new!'" & exit
Эта последовательность запускает новый процесс bash в фоновом режиме с командой и завершает текущий. Однако важно помнить, что в этом случае новый bash не наследует идентичного состояния по отношению к текущему процессу.
Заключение
Таким образом, способность управлять потоками выполнения через командную оболочку bash требует как теоретических знаний об операционной системе, так и практических навыков написания скриптов. Понимание, когда следует использовать fork
, exec
, или kill
, как оперировать между процессами и как их контролировать, критически важно для успешной автоматизации и управления средой выполнения. Изложенные концепции не только обеспечивают более глубокое понимание работе bash-скриптов, но и помогают в эффективной разработке устойчивых и надёжных решений в области программирования и управления IT-системами.