Почему функция не вернется, пока не закончится фоновый процесс?

21

Рассмотрим этот скрипт:

#!/bin/bash
function start {
  leafpad &
  echo $!
}
PID=$(start)
echo "PID is $PID"

Сценарий не продолжается после закрывающей фигурной скобки, пока не завершится процесс листовки, даже если это фоновый процесс.

Почему это? Можно ли запустить фоновый процесс из функции?

user234565
источник

Ответы:

22

Функция возвращается, но подстановка команды блокируется, потому что вы создали фоновое задание, но у вас все еще открыт stdout fd. Просто закройте его, добавив >/dev/nullперед &.

#!/bin/bash
function start {
  leafpad >/dev/null &
  echo $!
}
PID=$(start)
echo "PID is $PID"

Если вы хотите, чтобы у вашего процесса были также закрыты stdin, stdout, stderr, используйте это:

leafpad >/dev/null 0>&1 2>&1 &

Это закроет stdin (0), stdout (1) и stderr (2), затем background (&). Кроме того, при использовании этих потоковых перенаправлений не забывайте, что они «дублированы», то есть дублируются в порядке выполнения.

1>/dev/null 2>&1

а также

2>&1 1>/dev/null

не то же самое! В первом случае вы дублируете поток в / dev / null (что вам и нужно), во втором вы дублируете / dev / stdout в stderr, а затем закрываете stdout. Таким образом, любое отправленное сообщение stderrпоявится в вашей консоли.

Адриен М.
источник
Подтверждено в моей системе
user120161
10
Вы не закрываете потоки, вы перенаправляете их.
dcat
4
закрыть; n>&-где nдескриптор файла.
dcat
1
@dcat: Да, но перенаправление в / из /dev/nullне приведет к ошибкам ввода-вывода, когда процесс попытается записать свой стандартный вывод, но обнаружит, что 1это недопустимый FD. Так что терминология в посте неверна, а не фактическое программирование на bash. (На самом деле, дублирование FD с 1 по 0 означает, что stdin будет дескриптором файла, с O_RDONLYкоторым открывается файл , что, вероятно, приведет к ошибке (а не к желаемому отсутствию байтов), когда процесс попытается прочитать.) Например, wc >/dev/null 0>&1->wc: standard input: Bad file descriptor
Peter Кордес
1
@PeterCordes - Закрытие старого дескриптора и перенаправление нового не обязательно должны быть взаимоисключающими. exec <&- >&- <>/dev/null >&0обрабатывает stdin / out довольно исчерпывающе. Это имеет значение zshпо крайней мере в том, что будет сортировать все открытия по одному и тому же дескриптору автоматически, когда установлено multios.
mikeserv