Я использую Ubuntu 16.04, используя эмулятор терминала.

У меня есть два скрипта: a.sh и b.sh. a.sh вызывает b.sh.

a.sh

echo "a is running"
sleep 5s
bash b.sh
sleep 5s
echo "a is still running"

b.sh

echo "b is now running"
sleep 1000s
echo "Bananas"

Есть ли способ запустить a.sh, остановить b.sh до его завершения (до того, как он напечатает бананы) и позволить a.sh выполнить до завершения и напечатать "a все еще работает"?

Я попытался поместить pkill -f b.sh внутрь a.sh, но и a.sh, и b.sh останавливаются до завершения a.sh.

6
G-Man Says 'Reinstate Monica' 4 Фев 2018 в 08:42
3
Что произошло, когда вы попробовали pkill -f b.sh в сценарии a.sh?
 – 
Kusalananda
3 Фев 2018 в 23:58
Что говорит @Kusalananda
 – 
k.Cyborg
4 Фев 2018 в 00:14
В зависимости от платформы ваши сценарии либо работают нормально, либо терпят неудачу, потому что b.sh уже завершился до запуска строки pkill. Я действительно не думаю, что какой-либо из этих результатов неверен, но это означает, что то, что работает, будет зависеть от того, на какую платформу вы ориентируетесь. Тем не менее, я не думаю, что понимаю, что вы пытаетесь сделать или пытаетесь протестировать, и я подозреваю, что ваш тестовый сценарий на самом деле не представляет то, что вы хотите.
 – 
Michael Homer
4 Фев 2018 в 01:32
Было бы полезно отредактировать полученное вами сообщение/расшифровку выполнения вами команд.
 – 
Michael Homer
4 Фев 2018 в 01:34
Интересная головоломка у вас получилась!
 – 
G-Man Says 'Reinstate Monica'
4 Фев 2018 в 10:36

4 ответа

В какой-то момент вы были близки к тому, чего, кажется, хотите.

a.sh

#!/bin/sh

echo "a.sh running"
sleep 10s
(sleep 10s; pkill -f b.sh) &
bash b.sh
sleep 10s
pkill -f b.sh
echo "a.sh still running"

b.sh    (то же, что и ваша версия)

echo "b is now running"
sleep 1000s
echo "Bananas"

Строка (sleep 10s; pkill -f b.sh) & в a.sh создает подоболочку, которая спит в течение десяти секунд, а затем убивает b.sh. Затем он помещает эту подоболочку в фоновый режим, так что a.sh может продолжать работать. Затем a.sh запускает b.sh, который запускает sleep 1000s. Десять секунд спустя sleep 10s в подоболочке завершается, и он (подоболочка) запускает pkill -f b.sh, убивая процесс b.sh. a.sh затем возобновляет работу. Второй pkill -f b.sh ничего не делает, поскольку процесс b.sh уже завершен.

Вот что происходит при запуске:

$ ./a.sh
a.sh running                                              # immediately
b is now running                                          # about 10 seconds after the above
./a.sh: line 6: 13684 Terminated            bash b.sh     # about 10 seconds after the above
a.sh still running                                        # about 10 seconds after the above
$                                                         # immediately after the above

Это имеет то преимущество, что a.sh может немедленно возобновить работу. если b.sh заканчивается быстро. Только если b.sh работает более 10 секунд, срабатывает pkill. В другом ответе a.sh должен сидеть без дела в то время как sleep перед pkill занимает время, даже если b.sh уже завершено.


Другой пользователь опубликовал комментарий (сейчас удален), говорящий

я получил

./a.sh
a.sh running
b is now running
Terminated

не то, что вы сообщаете. Любая идея?

Это была темная и штормливая ночь. Внезапно раздался выстрел!

Это было мрачно и Бурная ночь, Снупи Чарльз М. Шульц

Аргумент для pgrep и pkill является регулярным выражением; в частности, расширенное регулярное выражение1. Попробуйте запустить cat и cut одновременно2. Затем выполните pgrep c.t — он выведет два PID, и вы можете подтвердить ps, что это cat и cut.

Если бы вы сделали в точности то, что я сказал, Вы действительно должны были получить те же результаты, что и я. Но я готов поспорить на 42 зоркмида, что вы сделали что-то другое. Вы либо:

  1. Начал a.sh с #!/bin/bash как девчонку (первая строка) вместо #!/bin/sh или
  2. Запустил a.sh с bash a.sh вместо ./a.sh.

Внезапно раздался выстрел!

b.sh — регулярное выражение, соответствующее b. за которым следует любой символ, за которым следует sh. Соответствует самому себе (b.sh), а также такие вещи, как b!sh, b~sh, b&sh, b7sh и bush. И … … (подождите) … … это также соответствует bash!

Итак, pkill -f b.sh убивает все процессы bash, а также любые другие, в именах которых есть b.sh. Когда я запускал то, что запускал, моя оболочка входа (который хотя бы частично невосприимчив к pkill) был bash, но a.sh работал под управлением /bin/sh (но b.sh работал под управлением bash). Команда pkill -f b.sh убила процесс bash b.sh потому что он содержит оба bash и b.sh. Но /bin/sh, на котором работал a.sh, не пострадал.

Предполагая, что вы запустили его так, как я считаю, вы запустили его (любой из двух вариантов), все процессы сценария выполнялись bash. Итак, команда pkill -f b.sh убила все процессы скрипта.

Замените pkill -f b.sh на pkill -f 'b\.sh' (исключая точку, чтобы она соответствовала только точке, а не любому символу), и вы должны получить правильные результаты.
________
1 pgrep(1)
2 Если у вас есть два терминала (окна оболочки), запустить cat | cut -f1 (с вводом из терминала) в одном из них, и p-команды в другом. Если у вас только один терминал, запустить sleep 42 | cat | cut -f1 & (без sleep конвейер немедленно рухнет), а затем запустите p-команды в том же окне.

4
G-Man Says 'Reinstate Monica' 5 Фев 2018 в 00:00

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

Поскольку b.sh запускается как фоновое задание, у вас есть доступ к его идентификатору процесса (PID), что означает, что вы можете использовать kill для подачи сигнала. Это безопаснее, чем использование pkill, которое потенциально будет сигнализировать другим процессам, которые соответствуют заданному вами шаблону.

Вы можете сделать следующее в a.sh:

#!/bin/sh

echo "A: I'm alive!"
./b.sh & bpid=$!

echo 'A: Sleeping for a short while'
sleep 2

echo 'A: Signalling B'
kill "$bpid"

echo "A: I'm done here"

$! будет расширен до PID последней запущенной фоновой задачи.

Пример b.sh:

#!/bin/sh

echo "B: I'm alive!"

echo 'B: Sleeping for longer than A does'
sleep 10

echo "B: I'm done here"

Запуск это:

$ ./a.sh
A: I'm alive!
A: Sleeping for a short while
B: I'm alive!
B: Sleeping for longer than A does
A: Signalling B
A: I'm done here

Обратите внимание, что b.sh может фактически не завершиться, пока не будет выполнен его вызов sleep. Это относится к bash, dash, zsh и pdksh, но не к ksh93 (с которым b.sh завершается немедленно).

0
Kusalananda 5 Фев 2018 в 13:35

Я использовал метод ниже. Добавлена ​​одна дополнительная строка в b.sh, которая заставляет работать в соответствии с требованиями.

Скрипт a.sh будет таким же

script a.sh

echo "a is running"
sleep 5s
bash b.sh
sleep 5s
echo "a is still running"

================================================== ===

script b.sh

echo "b is now running"
sleep 20s

exit

==>команда выхода выйдет из скрипта

echo "Bananas"

Выход

   a is running
b is now running
a is still running
-1
Praveen Kumar BS 5 Фев 2018 в 09:37
Вам по-прежнему необходимо указывать регулярное выражение для grep: | grep -i 'b\.sh' |, иначе вы рискуете вызвать kill в оболочке bash.
 – 
ImHere
4 Фев 2018 в 20:21
Итак, вместо того, чтобы a.sh остановить b.sh, как, кажется, хочет ОП, вы предлагаете остановить b.sh себя? Вы знаете, что есть гораздо более короткая команда, которая делает что-то подобное: exit. Почему бы просто не поместить команду exit в середине b.sh?
 – 
G-Man Says 'Reinstate Monica'
4 Фев 2018 в 21:41

Линия:

bash b.sh

Будет ждать завершения сценария b.sh, в сценарии нет возможности что-либо сделать до завершения b.sh, потому что a.sh не имеет контроля.

Чтобы сценарий b.sh перешел в фоновый режим и вернул управление a.sh, нужно использовать подобное:

bash b.sh &

Это немедленно возвращает управление a.sh, и у нас может быть такой сценарий:

#!/bin/bash 

echo "a is running"
sleep 1s
bash b.sh &
sleep 2
pkill -f 'b\.sh'
sleep 1s
echo "a is still running"

Пока скрипт b.sh продолжает быть:

#!/bin/bash
echo "b is now running"
sleep 1000s
echo "Bananas"

При выполнении вы получите:

$ ./a.sh
a is running
b is now running
./a.sh: line 7:  8424 Terminated              bash b.sh
a is still running

Чтобы избежать «сообщения управления заданием», используйте вспомогательную оболочку, измените bash b.sh & на:

(bash b.sh &)

И, при выполнении, вы получите:

$ ./a.sh
a is running
b is now running
a is still running

Редактировать:

Проблема с ответом «Завершено», о котором вы сообщаете, заключается в том, что отправленная вами команда pkill pkill -f b.sh использует аргумент в качестве регулярного выражения. Это регулярное выражение соответствует b, за которым следует любой символ ., за которым следует sh. Это соответствует имени «bash», которое означает, что программа начала работать, а также a.sh, что объясняет, почему a.sh завершается. Измените команду pkill на:

pkill -f 'b\.sh'

Чтобы убедиться, что совпадающее регулярное выражение представляет собой простую строку «b.sh».

Вы можете увидеть процессы, которым будет соответствовать pkill, поместив это:

ps -f -o pid,cmd,args |grep 'b.sh'

Непосредственно перед пикиллом.

Измените его на grep 'b\.sh', чтобы узнать, что на самом деле он соответствует только одному pid.

3
Community 11 Июн 2020 в 17:16
Спасибо - но это не сработало - я получил "Завершено" после того, как "b сейчас работает" - b.sh должно быть остановился - a.sh, казалось, остановился до завершения - никогда не получал "a все еще работает" - то же самое, что и произошло с моими вышеуказанными сценариями. Любые другие предложения?
 – 
speld_rwong
4 Фев 2018 в 07:12
Добавлено редактирование, объясняющее, почему вы получаете Terminated: цитата 'b\.sh', чтобы этот шаблон не соответствовал bash. @speld_rwong
 – 
ImHere
4 Фев 2018 в 09:28