Подтвердить что ты не робот

Bash фоновый процесс изменить глобальную переменную

У меня есть глобальное var foo = "некоторое значение" и фоновый процесс back_func, я хочу, чтобы фоновый процесс получил доступ к $foo и изменил его значение, что можно увидеть в основном процессе. Это что-то вроде следующего:

#!/bin/bash
foo=0

function back_func {
     foo=$(($foo+1))
     echo "back $foo"
}

(back_func) &
echo "global $foo"

Результат выше script равен

global 0
back 1

Как я могу получить результат глобального и обратного - это "1"?, то есть изменение основного состояния может вернуться к основному процессу.

4b9b3361

Ответ 1

Рандеву

Если вы хотите иметь два независимых процесса, которые могли бы обмениваться данными, вам нужно разместить rendez-vous где-то, к чему может добраться оба процесса.

Это может быть простой файл, fifo pipe, unix-сокет, сокет TCP или, возможно, еще (порт Rexx).

и другие

Bash не имеют эквивалента для порта rexx, поэтому есть небольшой пример с использованием файла rendez-vous, который работает (на моем Linux).

Я использую общую память /dev/shm, чтобы уменьшить нагрузку на диск.

Простой пример счетчика

$ back_func() {
    while :;do
        echo $(($(</dev/shm/foo)+1)) >/dev/shm/foo;
        sleep .3;
      done;
}

Разрешить воспроизведение

$ echo 1 >/dev/shm/foo
$ back_func &

$ echo $(</dev/shm/foo)
4

$ echo $(</dev/shm/foo)
21

Теперь остановитесь:

$ fg
back_func
^C

или

$ kill $!
$
[1]+  Terminated              back_func

Несколько переменных

Для того, чтобы иметь много vars, это может быть красиво:

$ back_func() {
    declare -A MYGLOBAL
    local vars
    while :; do
        ((MYGLOBAL["counter"]++))
        IFS=\ / read -a vars <<< "$(</proc/uptime) $(</proc/loadavg)"
        MYGLOBAL["uptime"]=$vars
        MYGLOBAL["idle"]=${vars[1]}
        MYGLOBAL["l01m"]=${vars[2]}
        MYGLOBAL["l05m"]=${vars[3]}
        MYGLOBAL["l15m"]=${vars[4]}
        MYGLOBAL["active"]=${vars[5]}
        MYGLOBAL["procs"]=${vars[6]}
        MYGLOBAL["lpid"]=${vars[7]}
        MYGLOBAL["rand"]=$RANDOM
        MYGLOBAL["crt"]=$SECONDS
        declare -p MYGLOBAL > /dev/shm/foo
        sleep 1
    done
}

Тогда

$ back_func &
[1] 27429
$ . /dev/shm/foo
$ echo ${MYGLOBAL['counter']}
5
$ echo ${MYGLOBAL['lpid']}
27432

и оттуда, почему бы и нет:

$ dumpMyGlobal() {
    . /dev/shm/foo
    printf "%8s " ${!MYGLOBAL[@]}
    echo
    printf "%8s " ${MYGLOBAL[@]}
    echo
}

$ dumpMyGlobal
    l15m   uptime      crt    procs     lpid   active     rand     idle     l05m
  counter     l01m 
    0.42 13815568.06       95      554      649        1    31135 21437004.95   
  0.38       73     0.50 
$ dumpMyGlobal
    l15m   uptime      crt    procs     lpid   active     rand     idle     l05m
  counter     l01m 
    0.41 13815593.29      120      553      727        2     3849 21437046.41   
  0.35       98     0.33 

или

$ dumpMyGlobal() {
    . /dev/shm/foo
    sort <(
        paste <(
            printf "%-12s\n" ${!MYGLOBAL[@]}
          ) <(printf "%s\n" ${MYGLOBAL[@]})
    )
}

$ dumpMyGlobal
active              1
counter             297
crt                 337
idle                21435798.86
l01m                0.40
l05m                0.44
l15m                0.45
lpid                30418
procs               553
rand                7328
uptime              13814820.80

Получить переменную с моментальным снимком

и, наконец, getMyGlobalVar функция

$ declare -A MYGLOBALLOCK   # snapshot variable
$ getMyGlobalVar () { 
    local i sync=false
    [ "$1" == "--sync" ] && shift && sync=true
    if [ -z "${MYGLOBALLOCK[*]}" ] || $sync; then
        . /dev/shm/foo
        for i in ${!MYGLOBAL[@]}
        do
            MYGLOBALLOCK[$i]=${MYGLOBAL[$i]}
        done
    fi
    echo ${MYGLOBALLOCK[$1]}
}

потребует флаг --sync для повторного чтения rendez-vous, чтобы вы могли смотреть вокруг каждого поля из того же моментального снимка.

$ getMyGlobalVar --sync idle
362084.12

$ getMyGlobalVar idle
362084.12

$ getMyGlobalVar rand
1533

$ getMyGlobalVar rand
1533

$ getMyGlobalVar --sync rand
43256

$ getMyGlobalVar idle
362127.63

Полноценный образец:

Существует полный образец: bash_ipc_demo

Вы можете использовать:

wget http://f-hauri.ch/vrac/bash_ipc_demo

source bash_ipc_demo
back_func help
Usage: bash [-q] [start|stop|restart|status|get|dump|help]

back_func status
Background loop function is not running.

back_func start

back_func status
Background loop function (19939) is running.

Оттуда, если вы source bash_ipc_demo в другом терминале, вы можете сделать список в них.

Вы даже можете закрыть первый терминал.

back_func dump
backFunc_count                     13
backFunc_now      2016-04-06 17:03:19
backFunc_pid                    19939
backFunc_running                  yes
backFunc_start    2016-04-06 17:03:07
cpu_numcores                        2
loadavg_15min                    0.44
loadavg_1min                     0.66
loadavg_5min                     0.54
loadavg_active                      1
loadavg_last_pid                20005
loadavg_process                   650
random                        3714432
uptime_idle                 425499.43
uptime_up                   495423.53
uptime_usage1sec                 9.90
uptime_usage                    57.06
uptime_useGraph  57.06 8.91 7.50 6.93 12.00 9.41 7.84 9.90 7.50 11.88 7.92 9.31 
9.90 

Затем вы можете получить одно значение

back_func get backFunc_pid newVar
echo $newVar 
19939

или построить график последней минуты:

back_func get uptime_useGraph vars;path="M0,0";x=0;for y in $vars;do
    printf -v step "L%.2f,%.2f L%.2f %.2f " $((x++*3)) $y $[3*x] $y
    path+=" $step"
  done;path+=" L$((3*x)),0 z";echo "<svg width=\"180\" height=\"100\">
        <rect style=\"fill: #DDD;\" width=\"180\" height=\"100\"></rect>
        <path style=\"fill: #08B;\" transform=\"matrix(1,0,0,-1,0,100)\" 
         d=\"$path\"></path></svg>" |
    inkscape -z --export-png=lastMinute.png /dev/stdin;eog lastMinute.png

** (inkscape:22147): WARNING **: Format autodetect failed. The file is being opened as SVG.
Background RRGGBBAA: ffffff00
Area 0:0:180:100 exported to 180 x 100 pixels (90 dpi)
Bitmap saved as: lastMinute.png

Тогда:

back_func stop  
back_func get backFunc_end
2016-04-06 17:16:08

Ответ 2

В соответствии с Bash ручным здесь,

Если команда завершена оператором управления '&, оболочка выполняет асинхронную команду в подоболочке.

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

Ответ 3

Если основной процесс (пусть его называть main.sh) - это еще один периодический запуск bash script, тогда вы можете просто иметь другой script (позвоните ему в other.sh), напишите значение в файл (позвоните в этот файл value.sh).

other.sh

#! /bin/bash  
echo "SOME_VAR=42" > /tmp/value.sh

main.sh

#! /bin/bash  
. /tmp/value.sh  
# Now you can use SOME_VAR