Ctrl-C з двома одночасними командами в bash


15

Я хочу запустити дві команди одночасно в bash на машині Linux. Тому в своєму ./execute.shбаш-скрипті я помістив:

command 1 & command 2
echo "done"

Однак, коли я хочу зупинити скрипт bash і натиснути Ctrl+ C, лише друга команда зупиняється. Перша команда продовжує працювати. Як переконатися, що повний скрипт bash зупинений? Або в будь-якому випадку, як зупинити обидві команди? Тому що в цьому випадку незалежно від того, як часто я натискаю Ctrl+ Cкоманда продовжує працювати, і я змушений закрити термінал.


Складіть подальше запитання в окреме питання та видаліть тут. Тепер для відвідувача незрозуміло, чи відповіли на обидва питання, чи просто на основне питання.
Зельда

Відповіді:


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 kill ... kill 0} command001 command1 & command2 Я пропустив команду wait, оскільки сценарій дозволений для продовження, коли команда2 закінчена. Однак здається, що команда 1 починається, коли я запускаю сценарій. Чи можу я це виправити? Дякую
maero21

command1 запускається безпосередньо після завершення command001. ви можете використовувати set -xна початку вашого сценарію для друку виконуваних команд.
michas

6

Коли викладаєте команду на задній план із сценарію, PID не відображається на екрані. Ви можете використовувати вбудовану змінну, $!яка зберігає PID останнього процесу, щоб ви могли захопити PID команди1.

command1 &
echo $!

відлунюватиме PID команди1.

Bash також забезпечує вбудовану пастку, яку ви можете використовувати для реєстрації послідовності команд, які потрібно запустити, коли приймаються певні сигнали. Ви можете використовувати це для того, щоб зловити команду SIGINT і kill1 перед виходом із головного сценарію, наприклад

#!/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: Так, але ОП виконує свої команди в сценарії (./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 Parale_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 &

Bash дасть вам обидва PID, готові вбити. Ще одна хитрість полягає в тому, щоб повернути 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-process/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.