Многопроцессорность с общей переменной в bash
Я пытаюсь добиться динамического прогресса в bash-скрипте, который мы видим при установке новых пакетов. Чтобы сделать это, случайная задача должна вызвать сценарий выполнения в качестве фоновой задачи и передать ему несколько целочисленных значений.
Первый скрипт использует канал для подачи второго.
#!/bin/bash
# randomtask
pbar_x=0 # percentage of progress
pbar_xmax=100
while [[ $pbar_x != $pbar_xmax ]]; do
echo "$pbar_x"
sleep 1
done | ./progressbar &
# do things
(( pbar_x++ ))
# when task is done
(( pbar_x = pbar_xmax ))
Следовательно, второй скрипт должен постоянно получать целое число и печатать его.
#!/bin/bash
# progressbar
while [ 1 ]; do
read x
echo "progress: $x%"
done
Но здесь второй скрипт не получает значения при их обновлении. Что я сделал не так?
2 ответа
Я на WSL, что означает, что я не могу использовать mkfifo. И, кажется, coproc отлично отвечал моим потребностям, поэтому я искал и в конце концов нашел это: использование coproc с примерами [bash-hackers wiki].
Мы начинаем процесс с coproc
и перенаправить его вывод на стандартный вывод:
{ coproc PBAR { ./progressbar; } >&3; } 3>&1
Затем мы можем получить доступ к его входу и выходу через файловые дескрипторы ${PBAR[0]}
(вывод) и ${PBAR[1]}
(Вход)
echo "$pbar_x" >&"${PBAR[1]}"
randomtask
#!/bin/bash
pbar_x=0 # percentage of progress
pbar_xmax=100
{ coproc PBAR { ./progressbar; } >&3; } 3>&1
while (( pbar_x <= 10)); do
echo $(( pbar_x++ )) >&"${PBAR[1]}"
sleep 1
done
# do things
echo $(( pbar_x++ )) >&"${PBAR[1]}"
# when task is done
echo $(( pbar_x = pbar_xmax )) >&"${PBAR[1]}"
индикатор
#!/bin/bash
while read x; do
echo "progress: $x%"
done
Обратите внимание, что:
Ключевое слово coproc не указано в POSIX(R).
Ключевое слово coproc появилось в Bash версии 4.0-alpha
Это не может работать, while
цикл работает в подпроцессе, изменения в основной программе никак не повлияют на него.
Существует несколько механизмов IPC, здесь я использую именованный канал (FIFO):
pbar_x=0 # percentage of progress
pbar_xmax=100
pipename="mypipe"
# Create the pipe
mkfifo "$pipename"
# progressbar will block waiting on input
./progressbar < "$pipename" &
while (( pbar_x != pbar_xmax )); do
#do things
(( pbar_x++ ))
echo "$pbar_x"
sleep 1
# when task is done
#(( pbar_x = pbar_xmax ))
done > "$pipename"
rm "$pipename"
Я тоже модифицировал progressbar
:
# This exits the loop when the pipe is closed
while read x
do
echo "progress: $x%"
done
С третьим скриптом вы можете использовать вместо процесса подстановку.