О, это хорошо.
Это связано с тем, как работают группы процессов, как работает bash при вызове в качестве неинтерактивной оболочки с -c
и эффектом &
в командах ввода.
Ответ предполагает, что вы знакомы с тем, как работает управление заданиями в UNIX; если вы этого не сделаете, вот представление высокого уровня: каждый процесс принадлежит к группе процессов (процессы в той же группе часто помещаются там как часть командного конвейера, например, cat file | sort | grep 'word'
будет размещать процессы с cat(1)
, sort(1)
и grep(1)
в одна и та же группа процессов). bash
- это процесс, подобный любому другому, и он также относится к группе процессов. Группы процессов являются частью сеанса (сеанс состоит из одной или нескольких групп процессов). В сеансе имеется не более одной группы процессов, называемой группой процессов переднего плана и, возможно, многими группами фонового процесса. Группа процессов переднего плана управляет терминалом (если имеется терминал управления, подключенный к сеансу); руководитель сеанса (bash) перемещает процессы от фона на передний план и от переднего плана до фона с tcsetpgrp(3)
. Сигнал, отправленный в группу процессов, доставляется каждому процессу в этой группе.
Если концепция групп процессов и управления заданиями является совершенно новой для вас, я думаю, вам нужно будет прочитать об этом, чтобы полностью понять этот ответ. Огромный ресурс для изучения этого - глава 9 of Расширенное программирование в среде UNIX (3-е издание).
Это, как говорится, давайте посмотрим, что здесь происходит. Мы должны скомпоновать каждый кусочек головоломки.
В обоих случаях удаленная сторона ssh вызывает bash(1)
с -c
. Флаг -c
вызывает bash(1)
для запуска в качестве неинтерактивной оболочки. Из: страница руководства
Интерактивная оболочка один запускается без аргументов без опций и без опции -c которой стандартный ввод и ошибок оба подключены к клеммам (как определено isatty (3)), или один запустил с опцией -i.PS1 установлен, а $ - включает i, если bash имеет значение , что позволяет сценарию оболочки или загрузочному файлу протестировать это состояние .
Кроме того, важно знать, что управление заданиями отключено, когда Баш запускается в режиме неинтерактивного. Это означает, что bash не будет создавать отдельную группу процессов для запуска команды, поскольку управление заданиями отключено, нет необходимости перемещать эту команду между передним и задним фонами, поэтому она может просто оставаться в той же группе процессов, что и bash , Это произойдет, если вы принудительно назначили PTY на ssh с -t
.
Однако использование &
имеет побочный эффект, из-за которого оболочка не ожидает завершения команды (даже если управление заданиями отключено). Из: страница руководства
Если команда завершается оператором & управления, оболочка выполняет команду в фоновом режиме в подоболочки. Оболочка делает , не дожидаясь завершения команды, а статус возврата равен 0. Команды, разделенные символом a; выполняются последовательно; оболочка ждет для каждой команды, чтобы завершить по очереди. Статус возврата - это выход статус последней выполненной команды.
Таким образом, в обоих случаях, Баш не будет ждать выполнения команды, и touch(1)
будет выполнен в той же группе процессов, как bash(1)
.
Теперь подумайте, что произойдет, когда лидер сеанса выйдет. Цитируя setpgid(2)
страницы руководства:
Если сеанс имеет контрольный терминал, а CLOCAL флаг для этого терминала не установлен, и терминал зависание происходит, то лидер сеанса отправляется SIGHUP. Если лидер сеанса выйдет, то сигнал SIGHUP также будет отправлен каждому процессу в процессе переднего плана группы управляющего терминала.
(выделено мной)
Когда вы не используете -t
Когда вы не используете -t
, нет выделения PTY на удаленной стороне, так Баш не и фактически не создается новый сеанс. Поскольку sshd работает как демон, процесс bash, который forked + exec() 'd, не будет иметь управляющий терминал. Таким образом, даже если оболочка завершается очень быстро (вероятно, до touch(1)
), нет SIGHUP
, отправленных в группу процессов, потому что bash не был лидером сеанса (и нет управляющего терминала). Так что все работает.
При использовании -t
-t
силы распределения PTY, что означает, что SSH удаленная сторона будет вызывать setsid(2)
, выделить псевдо-терминал + вилка новый процесс с forkpty(3)
, соединить вход ведущего устройства PTY, и вывод на конечные точки сокета, которые приводят к вашей машине, и, наконец, выполнить bash(1)
. forkpty(3)
открывает рабочую сторону PTY в раздвоенном процессе, который станет bash; поскольку нет контрольного терминала для текущего сеанса, и открывается терминальное устройство, устройство PTY становится управляющим терминалом для сеанса, а bash становится лидером сеанса.
Затем происходит то же самое: touch(1)
выполняется в той же группе процессов и т. Д., Yadda yadda. Дело в том, что на этот раз является лидером сеанса и контрольным терминалом. Итак, поскольку bash не беспокоится о ожидании из-за &
, когда он выходит, SIGHUP
доставляется в группу процессов, а touch(1)
умирает преждевременно.
О nohup
nohup(1)
здесь не работает, потому что есть еще условие гонки. Если bash(1)
прекращается до nohup(1)
имеет возможность создать необходимую обработку сигнала и перенаправление файла, это не будет иметь никакого эффекта (что, вероятно, что происходит)
Возможное исправление
Насильно повторного включения управления заданиями исправляет его. В bash вы делаете это с set -m
. Это работает:
ssh -t localhost 'set -m ; touch foobar &'
Или сила Баш ждать touch(1)
для завершения:
ssh -t localhost 'touch foobar & wait `pgrep touch`'
Я думаю, что это именно он. Фоновый процесс связан с tty, и tty dying убивает его. Попробуйте «nohup touch foobar &» и посмотрите, работает ли это и/или «коснитесь foobar/dev/null 2> & 1'. –
Оба «ssh -t localhost» touch foobar < /dev/null >/dev/null 2> & 1 & ''и' ssh -t localhost' nohup touch foobar & ''производят такое же поведение. – user414310
Не удается создать файл? Я получаю файл, созданный здесь даже для оригинальной версии для записи. –