Как запускать команды как в очереди

42

Мне нужно много копировать разные файлы в разные папки. Я могу добавить все свои команды копирования в скрипт bash и затем запустить его, но затем я должен дождаться его завершения, если я хочу добавить больше команд в эту «очередь» копирования.

Есть ли способ, которым я могу запускать команды в виде очереди и как бы добавлять команды в эту очередь во время работы?

Объясненный по-другому, я хочу начать долгосрочную задачу. Пока он работает, я хочу запустить еще один, который на самом деле не запускается, пока не будет сделан первый. А затем добавить еще один после этого последнего и так далее. Возможно ли это как-то?

Svish
источник
3
Могу ли я предложить вам принять ответ, который решает вашу проблему. Кажется, есть пара из них, которые будут работать для вас.
Холм
2
Да, вы можете, и я хотел. Проблема в том, что я спросил об этом, когда работал в компании, которая использовала Mac. Я там больше не работаю, и у меня нет компьютеров Mac, поэтому я не могу протестировать ни один из них. Не хочется отмечать один ответом, когда я не могу проверить, действительно ли он работает, поэтому сейчас я надеялся, что люди проголосуют за те ответы, которые для них подойдут, так что «ответ» может появиться таким образом. вместо.
Свиш

Ответы:

25

atУтилита, самая известная для запуска команд в заданное время, также имеет функцию очереди и может быть предложена для запуска команды в настоящее время. Он читает команду для запуска со стандартного ввода.

echo 'command1 --option arg1 arg2' | at -q myqueue now
echo 'command2 ...' | at -q myqueue now

Команда batchэквивалентна at -q b -m now( -mозначает, что выходные данные команды, если таковые имеются, будут отправлены вам по почте, как это делает cron). Не все варианты Unix поддерживают имена очередей ( -q myqueue); Вы можете быть ограничены одной вызванной очередью b. Linux atограничен однобуквенными именами очередей.

Жиль "ТАК - перестань быть злым"
источник
2
В Linux, по крайней мере, это, кажется, не ждет завершения предыдущей команды.
wds
@wds работает для меня на Debian wheezy. Где и как это сломалось для вас? Обратите внимание, что at ожидает только завершения команды; если команда запускает подпроцессы, которые переживают своего родителя, at не ожидает подпроцессов.
Жиль "ТАК - перестань быть злым"
Я попробовал на CentOS 6. Я тестировал с простым "sleep x". Я не уверен, что at делает, чтобы запустить это, но я предполагаю, что он запускает подоболочку, и эта подоболочка должна ждать результата (спящий вызов не возвращается немедленно).
WDS
Как и имена очередей, «сейчас» также не выглядит стандартным: bash на Ubuntu жалуется на это ...
winwaed
@winwaed О, nowнет -t now, извините. Я не заметил, что это неправильно в примере кода.
Жиль "ТАК - перестань быть злым"
23

Добавьте &в конец вашей команды, чтобы отправить ее в фоновом режиме, а затем waitна него, прежде чем запускать следующую. Например:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...
Vortico
источник
2
ура! этот синтаксис великолепен, когда вы что-то запускаете, а затем смотрите на stackoverflow, чтобы найти способ поставить что-то в очередь после него
Erik Aronesty
2
Если тем временем вы передумаете над какой-либо из этих команд, что можно сделать, чтобы прервать wait? Это кажется непроницаемым для всех моих убийств.
Кен Уильямс
1
Вы просто убиваете команду. waitПросто прекращает работу до тех пор , пока предыдущие закончить.
Герт Сендерби,
Будет ли это работать с nohup?
Майкл
11

Решения Брэда и Манкова являются хорошими предложениями. Другой вариант, похожий на их комбинацию, заключается в использовании GNU Screen для реализации вашей очереди. Преимущество этого в том, что вы можете работать в фоновом режиме, вы можете проверять это всякий раз, когда вы помещаете в очередь новые команды, просто вставляя их в буфер, который будет выполнен после выхода из предыдущих команд.

Первый забег:

$ screen -d -m -S queue

(кстати, сейчас хорошее время, чтобы поиграть с некоторыми классными . screenrc файлами )

Это вызовет сеанс фонового экрана для вас с именем queue.

Теперь поставьте в очередь столько команд, сколько хотите:

screen -S queue -X stuff "echo first; sleep 4; echo second^M"

Я делаю несколько команд в приведенном выше только для тестирования. Ваш вариант использования будет выглядеть примерно так:

screen -S queue -X stuff "echo first^M"
screen -S queue -X stuff "echo second^M"

Обратите внимание, что «^ M» в моей строке выше - это способ получить встроенную новую строку, которая будет интерпретирована позже после того, как экран вставит ее в существующую оболочку bash. Используйте «CTL-V», чтобы получить эту последовательность.

Было бы довольно легко сделать несколько простых сценариев оболочки для автоматизации этого и поставить команды в очередь. Затем всякий раз, когда вы хотите проверить состояние фоновой очереди, вы повторно присоединяете через:

screen -S queue -r

Технически, вам даже не нужно называть ваш сеанс экрана, и он будет работать нормально, но как только вы его зацепите, вы все равно захотите оставить его включенным. ;-)

Конечно, если вы сделаете это, другой хороший способ сделать это - назвать одну из текущих окон «очередь» и использовать:

screen -S queue -p queue -X stuff "command"
Иордания
источник
Похоже, что это в основном просто «набирает» команду в сеансе рабочего экрана, поэтому, когда оболочка снова получает контроль после завершения первой команды, эта команда запускается. Другими словами, это эквивалентно решению @ mankoff, но использует более изящный инструмент для набора текста.
Кен Уильямс
Довольно много - главное отличие в том, что экранное решение поддерживает лучшую автоматизацию. Вы можете делать некоторые вещи вручную, другие - с помощью скриптов, все с простым интерфейсом.
Джордан
@ Джордан Очень хорошо, это работает для меня. Но что такое «материал» для после -X?
Константин
@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - прочее все , что следует в терминал , как если бы вы ввели его)
Jordan
@ Джордан О, теперь я вижу. Это команда. Я думал, что это произвольная строка.
Константин
8

Есть утилита, которую я использовал с большим успехом именно для описанного вами варианта использования. Недавно я перенес свой основной ноутбук на новое оборудование, которое включало перенос некоторых файлов на NAS, а остальные - на новую машину.

Вот как я это сделал.

  1. Настройте все машины, подключенные к сетевому соединению, чтобы они могли связаться друг с другом.

  2. На компьютере, с которого вы перемещаете файлы (в дальнейшем называемом исходным компьютером), установите rsync apt-get install rsyncи Task Spooler с секретным соусом (веб-сайт http://vicerveza.homeunix.net/~viric/soft/ts/ ). Если вы используете Debian, имя пакета для tsis task-spoolerи исполняемый файл переименовываются, tspчтобы избежать конфликта имен с tsисполняемым файлом из moreutilsпакета. Пакет Debian, связанный с веб-сайтом, отлично устанавливается на Ubuntu dpkg -i task-spooler_0.7.3-1_amd64.debили аналогичном.

  3. Также убедитесь, что на всех машинах установлен SSH apt-get install openssh-server. На исходном компьютере вам нужно настроить SSH, чтобы разрешить вход без пароля на целевые компьютеры. Чаще всего будет использоваться метод аутентификации с открытым ключом ssh-agent( примеры приведены в https://www.google.se/search?q=ssh+public+key+authentication ), но иногда я использую более простой метод, который работает просто а также с аутентификацией по паролю. Добавьте следующее в вашу конфигурацию клиента SSH ( ~/.ssh/configили /etc/ssh/ssh_config):

    Host *
         ControlMaster auto
         ControlPath ~/.ssh/master-%r@%h:%p
    

    Затем откройте один терминал на исходном компьютере, войдите в систему ssh target1, выполните обычную аутентификацию и оставьте этот терминал открытым. Обратите внимание, что существует файл сокета с именем ~/.ssh/master-user@target1:22, это файл, который будет держать аутентифицированную основную сессию открытой и разрешать последующие соединения без пароля user(при условии, что соединение использует одно и то же целевое имя хоста и порт).

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

  4. Теперь запустите ts rsync -ave ssh bigfile user@target1:для одного файла или ts rsync -ave ssh bigdir user@target1:для каталога. При использовании rsync важно не включать косую черту в каталог ( bigdirпо сравнению с bigdir/), иначе rsync будет считать, что вы имели в виду эквивалент bigdir/*большинства других инструментов.

    Диспетчер задач вернет приглашение и позволит вам поставить в очередь многие из этих команд подряд. Проверьте очередь выполнения tsбез аргументов.

Диспетчер задач имеет много функций, таких как реорганизация очереди выполнения, запуск определенного задания только в случае успешного выполнения другого задания и т. Д. См. Справку ts -h. Иногда я проверяю вывод команды, пока он работает с ts -c.

Есть и другие способы сделать это, но для вашего случая использования они все включают диспетчер задач. Я решил использовать rsync поверх SSH для сохранения временных меток файлов, которых не было бы при копировании через SMB.

holmb
источник
спасибо за ваш ответ, о котором я не знал ts, он кажется очень мощным и простым в использовании, просто разветвленный на github, с автоинструкцией и дебианизацией.
Алекс
@ Алекс Я посмотрел на твой форк и особенно интересовался тем, что было сделано в отношении автоинструмента и дебианизации исходного источника, и, глядя на твои коммиты, он не был легко доступен как diff для оригинального источника. Не могли бы вы принудительно выдвинуть новые коммиты, которые на шаге 1. импортируют исходный код и 2. коммитят с вашими дополнениями? Это поможет кому-то просматривать ваш источник (легче) доверять вашим дополнениям к исходному источнику. Это один из немногих пакетов, которые я не устанавливаю из PPA или подобного, поэтому было бы неплохо собрать его самостоятельно с помощью своей вилки.
Холм
Вы знаете, что я должен поменять очки, я потерял часть вашего ответа о существующем task-spooler:-) ... в любом случае, это в вашем комментарии очень хорошее предложение, я сделаю это очень скоро.
Алекс
сделал, удалил и заново создал хранилище с прогрессивными коммитами, только пару ошибок (должно усвоить больше локальных коммитов перед push)
Alex
4

Мне тоже часто нужны такие вещи. Я написал небольшую утилиту под названием, afterкоторая выполняет команду всякий раз, когда завершается какой-то другой процесс. Это выглядит так:

#!/usr/bin/perl

my $pid = shift;
die "Usage: $0 <pid> <command...>" unless $pid =~ /^\d+$/ && @ARGV;

print STDERR "Queueing process $$ after process $pid\n";
sleep 1 while -e "/proc/$pid";
exec @ARGV;

Затем вы запускаете это так:

% command1 arg1 arg2 ...  # creates pid=2853
% after 2853 command2 arg1 arg2 ... # creates pid=9564
% after 9564 command3 arg1 arg2 ...

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

Кен Уильямс
источник
Хороший сценарий @ken. Хотя я бы порекомендовал попробовать Task Spooler, поскольку время от времени вам это требуется. Смотри мой ответ.
13
Выглядит аккуратно, спасибо. Кажется, единственное, чего не хватает в TS, - это возможность выполнять задания, которые не были запущены в TS. Хотя я думаю, вы могли бы начать новую работу TS, цель которой - просто дождаться завершения существующего процесса.
Кен Уильямс
Верно. Это полезный аспект вашего решения.
Холм
2

Command1 && Command2

  • «&&» означает, что команда2 выполняется только после того, как команда1 завершается с кодом возврата 0
  • Примечание: a || означает, что команда2 выполняется только после того, как команда1 завершается с кодом возврата, отличным от 0
naftuli
источник
1

Вы можете просто добавить команды в командную строку оболочки, в которой уже выполняется команда.

Либо наберите внимательно, либо напечатайте в другом месте, проверьте и вырежьте и вставьте. Даже если текущая команда издает строки вывода, вы все равно можете набрать что-то новое, нажать ввод, и она запустится, когда все команды будут запущены или введены до ее завершения.

Пример следует. Вы можете вырезать и вставить все это или ввести последующие команды во время выполнения 1-го (если вы действительно очень быстро печатаете) или, скорее всего, во время выполнения 2-го:

echo "foo"
sleep 10; echo "bar"
sleep 3
echo "baz"

источник
0

самый хакерский способ, который я могу придумать, - это поддерживать состояние очереди с помощью простых файлов блокировки в / tmp. когда file1.sh выполняет свои команды cp, он сохраняет файл блокировки (или жесткую ссылку) в / tmp. когда это будет сделано, удалите файл. любой другой скрипт должен искать в / tmp файлы блокировки с выбранной вами схемой именования. Естественно, это подвержено всевозможным сбоям, но настроить его тривиально и полностью выполнимо в bash.

Брэд Клоузи
источник
Трудно использовать на практике, так как требует, чтобы каждое задание, которое вы хотите запустить, должно быть изменено для сохранения файлов блокировки, и ему необходимо знать (или принимать в качестве параметров), какие файлы блокировки использовать. Когда это возможно, лучше иметь очереди вне работы.
Кен Уильямс