Многопроцессорность с общей переменной в 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

С третьим скриптом вы можете использовать вместо процесса подстановку.

Другие вопросы по тегам