Ответ 1
Я не тестировал это, но как насчет
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
Скобки означают выполнение в подоболочке, но это не должно повредить.
Я пытаюсь выполнить некоторые команды в паралели, в фоновом режиме, используя bash. Вот что я пытаюсь сделать:
forloop {
//this part is actually written in perl
//call command sequence
print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}
Часть между backticks (``) порождает новую оболочку и последовательно выполняет команды. Дело в том, что управление исходной программой возвращается только после выполнения последней команды. Я хотел бы выполнить весь оператор в фоновом режиме (я не ожидаю никаких значений вывода/возврата), и я бы хотел, чтобы цикл продолжал работать.
Вызывающая программа (та, которая имеет цикл) не заканчивается, пока не закончится все порожденные оболочки.
Я мог бы использовать потоки в perl, чтобы создавать разные потоки, которые вызывают разные оболочки, но это кажется излишним...
Можно ли запустить оболочку, дать ей набор команд и сказать, чтобы она переходила на задний план?
Я не тестировал это, но как насчет
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
Скобки означают выполнение в подоболочке, но это не должно повредить.
Спасибо Хью, что сделал это:
[email protected]:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
[email protected]:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
[email protected]:~$ stopped
[1]+ Done ( echo "started"; sleep 15; echo "stopped" )
[email protected]:~$
Другие идеи не работают, потому что они запускают каждую команду в фоновом режиме, а не последовательность команд (что важно в моем случае!).
Еще раз спасибо!
Другой способ - использовать следующий синтаксис:
{ command1; command2; command3; } &
wait
Обратите внимание, что &
идет в конце группы команд, а не после каждой команды. Точка с запятой после последней команды необходима, как и пробел после первой скобки и перед последней скобкой. wait
в конце гарантирует, что родительский процесс не будет уничтожен до завершения порожденного дочернего процесса (группа команд).
Вы также можете делать такие вещи, как перенаправление stderr
и stdout
:
{ command1; command2; command3; } 2>&2 1>&1 &
Ваш пример будет выглядеть так:
forloop() {
{ touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
for command in $commands
do
"$command" &
done
wait
Амперсанд в конце команды запускает его в фоновом режиме, а wait
ждет завершения фоновой задачи.
GavinCattell получил самый близкий (для bash, IMO), но, как отметил Mad_Ady, он не будет обрабатывать файлы "блокировки". Это должно:
Если ожидаются другие ожидающие действия, ожидания wait тоже будут ждать. Если вам нужно подождать только копии, вы можете накапливать эти PID и ждать только тех. Если нет, вы можете удалить 3 строки с помощью "pids", но это более общее.
Кроме того, я добавил проверку, чтобы полностью избежать копирования:
pids=
for file in bigfile*
do
# Skip if file is not newer...
targ=/destination/$(basename "${file}")
[ "$targ" -nt "$file" ] && continue
# Use a lock file: ".fileN.lock" for each "bigfileN"
lock=".${file##*/big}.lock"
( touch $lock; cp "$file" "$targ"; rm $lock ) &
pids="$pids $!"
done
wait $pids
Кстати, похоже, что вы копируете новые файлы в репозиторий FTP (или похожий). Если это так, вы можете рассмотреть стратегию копирования/переименования вместо файлов блокировки (но это другая тема).
Объект в bash, который вы ищете, называется Compound Commands
. См. Справочную страницу для получения дополнительной информации:
Составные команды Составная команда является одной из следующих:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell environment do not remain in effect after the command completes. The return status is the exit status of list. { list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharac‐ ters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace or another shell metacharac‐ ter.
Есть и другие, но это, вероятно, 2 наиболее распространенных типа. Первый, parens, будет запускать список команд последовательно в подоболочке, а второй, фигурные скобки, будет содержать список последовательностей в текущей оболочке.
% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013
% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
Запустите команду, используя задание at:
# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012
Результат будет отправлен на ваш счет по почте.
Я наткнулся на эту тему здесь и решил собрать фрагмент кода, чтобы порождать прикованные заявления в качестве фоновых заданий. Я тестировал это на BASH для Linux, KSH для IBM AIX и Busybox ASH для Android, поэтому я думаю, что можно с уверенностью сказать, что это работает в любой оболочке, подобной Bourne.
processes=0;
for X in `seq 0 10`; do
let processes+=1;
{ { echo Job $processes; sleep 3; echo End of job $processes; } & };
if [[ $processes -eq 5 ]]; then
wait;
processes=0;
fi;
done;
В этом коде выполняется несколько фоновых заданий до определенного предела одновременных заданий. Вы можете использовать это, например, для рекомпрессии большого количества gzipped файлов с помощью xz
без огромной совокупности процессов xz
, которые будут потреблять всю вашу память и заставляют ваш компьютер забрасывать: в этом случае вы используете *
как список for
и пакетное задание будут gzip -cd "$X" | xz -9c > "${X%.gz}.xz"
.
выполните команды в подоболочке:
(command1 ; command2 ; command3) &
Попробуйте поместить команды в фигурные скобки с помощью & s, например:
{command1 & ; command2 & ; command3 & ; }
Это не создает под-оболочку, но выполняет группу команд в фоновом режиме.
НТН
Я не знаю, почему никто не ответил с правильным решением:
my @children;
for (...) {
...
my $child = fork;
exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;
Это приводит к тому, что Perl запускает дочерние элементы для запуска каждой команды и позволяет подождать, пока все дети будут завершены, прежде чем продолжить.
Кстати,
print `some command`
и
system "some command"
выводит одно и то же содержимое в stdout, но первое имеет более высокие служебные данные, поскольку Perl должен отображать все выходные данные <some command
Викинг в цикле for:
for i in x; do ((a; b; c;)&); done
Пример:
for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done
На всякий случай, что кого-то все еще интересует, вы можете сделать это, не называя подоболочку следующим образом:
print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
Вы можете использовать команду GNU parallel
для параллельной работы заданий. Это безопаснее быстрее.
Я предполагаю, что вы пытаетесь скопировать несколько больших файлов из источника в пункт назначения. И для этого вы можете сделать это параллельно с инструкцией ниже.
$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'
Как мы использовали параметр -j0
, все файлы будут скопированы параллельно. В случае, если вам нужно уменьшить количество параллельных процессов, вы можете использовать -j<n>
, где <n>
- количество параллельных процессов, которые должны быть выполнены.
Параллельно будет также собирать выходные данные процесса и сообщать об этом последовательным образом (с параметром -k
), который не может выполнять другой механизм управления заданиями.
--eta
опция даст вам подробную статистику процесса, который происходит. Таким образом, мы можем знать, как процесс может быть завершен и сколько времени потребуется для завершения.
Вы можете передавать параметры в группу команд (с последовательными командами) и запускать их в фоновом режиме.
for hrNum in {00..11};
do
oneHour=$((10#$hrNum + 0))
secondHour=$((10#$hrNum + 12))
{ echo "$oneHour"; echo "$secondHour"; } &
wait
done