Для самого фона это приложение создает дочерний процесс и закрывает родительский. Оболочка знает о родительском процессе как о том, который она разветвила перед выполнением самой команды, но она не видит потомков или внуков, которые могли быть порождены этим процессом.
Более простой пример такой фоновой команды:
$ sh -c 'ps -j; sleep 10 &'
PID PGID SID TTY TIME CMD
6562 6562 14469 pts/13 00:00:00 sh
6563 6562 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
Мой zsh
знает об этом процессе sh
и его группе процессов.
Однако он видит только то, что процесс 6562 завершается. У него нет возможности узнать, что процесс 6562 породил процесс 6563.
$ ps -j
PID PGID SID TTY TIME CMD
6564 6562 14469 pts/13 00:00:00 sleep
6565 6565 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
Однако вы можете видеть, что запущенный процесс sleep
также находится в этой группе процессов 6562 (хотя нет ничего, что останавливает команды, запускающие новые группы процессов или даже сеансы (как обычно делают демоны)).
Эти группы процессов создаются только тогда, когда оболочка интерактивна.
Еще вы могли бы сделать следующее:
cmd | cat &
wait
Если процессы, порождаемые cmd, не закрывают свой стандартный вывод, тогда cat
не умирает, пока все они не умрут.
В настоящее время вы указываете своему сценарию обрабатывать каждый параметр как отдельное приложение. Итак, вы бежите:
$ vlc
Тогда:
$ video.mp4
Вы должны сделать свой сценарий более похожим на этот:
#!/bin/bash
$@ 2>/dev/null
Что, на мой взгляд, лучше подошло бы в качестве функции:
func_name() {
$@ 2>/dev/null
}
Обратите внимание, :это позволит вам запускать только одну команду за раз. Я не уверен, что вашим первоначальным намерением было передать несколько команд одновременно вашему сценарию, если это так, я удалю.
Есть две проблемы:
Использование $@
без кавычек.
Это заставит цикл перебирать vlc
и video.mp4
как два отдельных элемента, даже если они находятся в одной и той же строке в кавычках при вызове скрипта.
Использование команды в переменной.
Если команда представляет собой нечто более сложное, чем одна простая команда, то это не сработает. Вместо этого вам нужно будет eval
указать данную строку.
Принимая это во внимание, ваш скрипт может выглядеть так
#!/bin/sh
for cmd do # or: for cmd in "$@"; do
eval "$cmd" 2>/dev/null
done
Вызов этого как
./script 'echo "hello world"' 'vim "$HOME/.profile"' 'tr a-z A-Z <"$HOME/.profile" | grep -c EXPORT'
сначала запустит echo "hello world"
, а когда это завершится, откроется vim
для редактирования именованного файла в этой второй команде. Последняя команда более сложная, но обрабатывается тем фактом, что мы используем eval
(, она просто изменяет все буквы алфавита на прописные и подсчитывает, сколько раз строка EXPORT
встречается в файле ). Он запускается сразу после завершения сеанса vim
.
С этим можно даже сделать
./script 'd=$HOME' 'ls "$d"'
т. е. установить переменные, которые будут использоваться в последующих командах. Это работает, потому что команды, вызываемые eval
, выполняются в той же среде, что и скрипт. Это не сработает, если вы запустите команды как фоновые задачи, как следует из названия вашего вопроса.