Ctrl-C с двумя одновременными командами в bash

15

Я хочу запустить две команды одновременно в bash на машине с Linux. Поэтому в моем ./execute.shскрипте Bash я положил:

command 1 & command 2
echo "done"

Однако когда я хочу остановить скрипт bash и нажать Ctrl+ C, останавливается только вторая команда. Первая команда продолжает работать. Как мне убедиться, что полный скрипт bash остановлен? Или в любом случае как мне остановить обе команды? Потому что в этом случае, независимо от того, как часто я нажимаю Ctrl+, Cкоманда продолжает работать, и я вынужден закрыть терминал.

maero21
источник
Сделайте следующий вопрос отдельным вопросом и удалите его здесь. Теперь для посетителя неясно, был ли дан ответ на оба вопроса или только на начальный вопрос.
Зельда

Ответы:

17

Если вы печатаете

command 1 & command 2

это равно

command 1 &
command 2

то есть это будет запускать первую команду в фоновом режиме, а затем запускает вторую команду на переднем плане. Особенно это означает, что ваша echo "done"распечатана после command 2завершения, даже если command 1все еще работает.

Вы, вероятно, хотите

command 1 &
command 2 &
wait
echo "done"

Это запустит обе команды в фоновом режиме и будет ждать завершения обеих команд.


Если вы нажмете CTRL-C, это только отправит сигнал SIGINT на передний план процесса, т.е. command 2в вашей версии или waitв моей версии.

Я бы предложил установить ловушку так:

#!/bin/bash

trap killgroup SIGINT

killgroup(){
  echo killing...
  kill 0
}

loop(){
  echo $1
  sleep $1
  loop $1
}

loop 1 &
loop 2 &
wait

С помощью ловушки сигнал SIGINT, генерируемый CTRL-C, захватывается и заменяется killgroupфункцией, которая убивает все эти процессы.

Михась
источник
Спасибо за ваш ответ! У меня, однако, есть дополнительный вопрос. Теперь у меня есть следующий сценарий: trap killgroup SIGINT killgroup () {echo killing ... kill 0} command001 command1 & command2 Я пропустил команду ожидания, потому что сценарию разрешено продолжать работу после завершения команды command2. Однако кажется, что команда 1 уже запускается, когда я запускаю скрипт. Можно это исправить? Спасибо
maero21
command1 запускается сразу после завершения команды001. Вы можете использовать set -xв начале вашего скрипта для печати выполняемых команд.
Михас
6

При вводе команды в фоновом режиме из сценария PID не отображается на экране. Вы можете использовать встроенную переменную, $!которая хранит PID последнего процесса, чтобы вы могли захватить PID команды1.

command1 &
echo $!

повторил бы PID команды1.

Bash также предоставляет встроенную ловушку, которую вы можете использовать для регистрации последовательности команд, выполняемых при получении определенных сигналов. Вы можете использовать это для перехвата SIGINT и kill command1 перед выходом из основного скрипта, например

#!/bin/bash

onINT() {
echo "Killing command1 $command1PID too"
kill -INT "$command1PID"
exit
}

trap "onINT" SIGINT
command1 &
command1PID="$!"
comamnd2
echo Done

Теперь, пока команда 2 выполняется, нажатие Ctrl Cприведет к тому, что и команда 1, и команда 2 будут отправлены SIGINT.

Сообщество
источник
Вы также можете использовать %nсинтаксис для уничтожения определенных фоновых заданий в этой оболочке. Обычно вы выпускаете, kill %1чтобы убить последний и единственный фоновый процесс. При наличии дополнительных фоновых процессов jobsсначала используйте список.
9000
@ 9000: Да, но OP запускает свои команды в сценарии (./execute.sh), поэтому получение рабочих мест намного сложнее, конечно?
@lain: конечно. Я упустил момент о запуске из скрипта.
9000
5

Более новые версии GNU Parallel будут делать то, что вы хотите:

parallel ::: "command 1" "command 2"
echo "done"

Или, если commandостается прежним:

parallel command ::: 1 2
echo done

Посмотрите вступительное видео для быстрого ознакомления: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Пройдите учебник (man parallel_tutorial). Ты командная строка с любовью тебя за это.

Оле Танге
источник
1

Ctrl+ Cотправляет сигнал SIGINT на ваш фронт-процесс, который есть command2. command1выполняется в фоновом режиме, поэтому не касается входного потока.

Когда вы печатаете command1 &, bash должен дать вам PID процесса, что-то вроде [1234]. Чтобы убить этот процесс, вы можете использовать kill 1234. Теперь, если у вас есть PID обоих command1и command2(посмотрите ps -ef), вы можете использовать killдля их завершения:

kill pid1 pid2 pid3 ...

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

command1 & command2 &

Баш даст вам оба идентификатора, готовых быть убитыми. Еще один трюк - вернуть command1на передний план, как только вы убьете command2:

command1 & command2
# Hit Ctrl+C : command2 terminates.

fg # Bring back command1 to foreground.
# Hit Ctrl+C again, command1 terminates.

Более подробная информация доступна здесь: http://linuxg.net/how-to-manage-background-and-foreground-processes/

Джон У. Смит
источник