Запуск команд bash в фоновом режиме без печати заданий и идентификаторов процессов

83

Запустить процесс в фоновом режиме в bash довольно просто.

$ echo "Hello I'm a background task" &
[1] 2076
Hello I'm a background task
[1]+  Done                    echo "Hello I'm a background task"

Однако вывод подробный. В первой строке печатается идентификатор задания и идентификатор процесса фоновой задачи, затем у нас есть выходные данные команды, наконец, у нас есть идентификатор задания, его статус и команда, которая инициировала задание.

Есть ли способ подавить вывод выполнения фоновой задачи таким образом, чтобы вывод выглядел точно так же, как без амперсанда в конце? Т.е.:

$ echo "Hello I'm a background task" &
Hello I'm a background task

Причина, по которой я спрашиваю, заключается в том, что я хочу запустить фоновый процесс как часть команды завершения табуляции, поэтому вывод этой команды должен быть непрерывным, чтобы иметь какой-либо смысл.

Алекс Сперлинг
источник
Я полагаю, вам следует добавить 2> / dev / null ко всему, что вы делаете внутри сценариев завершения bash
см.

Ответы:

98

Не связано с завершением, но вы можете подавить этот вывод, поместив вызов в подоболочку:

(echo "Hello I'm a background task" &)
Димитр Радулов
источник
3
Спасибо, это определенно работает. К сожалению, при запуске подобного сценария из функции завершения bash кажется, что bash ожидает завершения подпроцесса, прежде чем выдает результаты завершения. Это прискорбно, так как это означает, что, похоже, невозможно запустить фоновые рабочие процессы с помощью завершения bash.
Alex Spurling
13
Вы не можете использовать это, если хотите, чтобы waitфоновое задание завершилось.
arekolek
13

Основываясь на ответе @hellter, это сработало для меня:

tyler@Tyler-Linux:~$ { echo "Hello I'm a background task" & disown; } 2>/dev/null; sleep .1;
Hello I'm a background task
tyler@Tyler-Linux:~$

Я не знаю причин этого, но я вспомнил из старого сообщения, что disown не позволяет bash выводить идентификаторы процессов.

Тизоид
источник
@Tyzoid. Я попробовал ваше решение в оболочке bash (v 4.2.37) и получил интересные результаты. 1. Я получил те же результаты, просто добавив ;в конце. (без сна) . 2. НО заметил, что когда я добавил echo STDERR 2>&1на "следующую" строчку, то [1]+ Done ...появилась вещь! . 3. Мое решение (как я уже отметил) работает kshи echo STDERR 2>&1выдает только вывод STDERR. Что ж, живи и учись для нас обоих. Всем удачи.
shellter 03
12

В некоторых более новых версиях bash и в ksh93 вы можете окружить его суб-оболочкой или группой процессов (т.е. { ... }).

/home/shellter $ { echo "Hello I'm a background task" & } 2>/dev/null
Hello I'm a background task
/home/shellter $
снаряд
источник
5
Когда я использую фигурные скобки, последняя строка вывода все еще отображается (я не уверен, почему вы ее не видите).
Alex Spurling
Я скопировал это в точности, но у меня это не работает, он все еще показывает результат, как говорит @AlexSpurling. Возможно, я делаю что-то не так, потому что другие проголосовали за это, но, боюсь, я должен дать -1.
Марк
1
@Mark: Я только что провел несколько быстрых тестов с этим, и я вижу, что проблема в том, что я продолжаю использовать его в ksh93+качестве основной оболочки. Этот код работает, как и было обещано в ksh. но поскольку OP помечен, bashя понимаю ваш дв. Смотрите результаты тестирования bash в ответе @Tyzoid. Всем удачи!
shellter 03
Поскольку вы ясно дали понять, что это не работает в bash, я удалю -1.
Марк
1
Для меня это отлично работает в функции Bash, также можно дождаться процесса, wait $!который, похоже, не относится к принятому ответу.
Jonatan Öström
6

Основываясь на приведенном выше ответе, если вам нужно разрешить stderr проходить из команды:

f() { echo "Hello I'm a background task" >&2; }
{ f 2>&3 &} 3>&2 2>/dev/null
пижамный
источник
Красивое использование перенаправлений. Тем не менее, в некоторых случаях вам понадобитсяdisown .
Tom Hale
6

Основываясь на этом ответе , я придумал более лаконичный и правильный:

silent_background() {
    { 2>&3 "$@"& } 3>&2 2>/dev/null
    disown &>/dev/null  # Prevent whine if job has already completed
}
silent_background date
Том Хейл
источник
идеальный! и многоразового использования для других вещей
user230910 02
5

Пытаться:

user@host:~$ read < <( echo "Hello I'm a background task" & echo $! )
user@host:~$ echo $REPLY
28677

И вы скрыли и вывод, и PID . Обратите внимание, что вы все еще можете получить PID из $ REPLY

mark_infinite
источник
1
Это потрясающе! Спасибо
Адхам Атта
4

Извините за ответ на старый пост, но я считаю, что это полезно для других, и это первый ответ в Google.

У меня возникла проблема с этим методом (подоболочки) и с использованием «подождите». Однако, когда я запускал его внутри функции, я смог сделать это:

function a {
    echo "I'm background task $1"
    sleep 5
}

function b {
    for i in {1..10}; do
        a $i &
    done
    wait
} 2>/dev/null

И когда я его запускаю:

$ b
I'm background task 1
I'm background task 3
I'm background task 2
I'm background task 4
I'm background task 6
I'm background task 7
I'm background task 5
I'm background task 9
I'm background task 8
I'm background task 10

И есть задержка в 5 секунд, прежде чем я верну свою подсказку.

Томагавк
источник
1

Решение подоболочки работает, но я также хотел иметь возможность ждать фоновых заданий (и не иметь в конце сообщения «Готово»). $!из подоболочки не является "ожидаемым" в текущей интерактивной оболочке. Единственное решение, которое сработало для меня, - это использовать мою собственную функцию ожидания, которая очень проста:

myWait() {
  while true; do
    sleep 1; STOP=1
    for p in $*; do
      ps -p $p >/dev/null && STOP=0 && break
    done
    ((STOP==1)) && return 0
  done
}

i=0
((i++)); p[$i]=$(do_whatever1 & echo $!)
((i++)); p[$i]=$(do_whatever2 & echo $!)
..
myWait ${p[*]}

Достаточно просто.

ExpertNoob1
источник