shell 如何在并行运行的脚本之间共享变量?

q8l4jmvw  于 2023-08-07  发布在  Shell
关注(0)|答案(1)|浏览(204)

我想运行两个bash脚本,同时由一个父脚本执行,该父脚本在所有这些脚本之间导出一个具有读写权限的变量。假设我有三个脚本:***parent.sh***、***children1.sh***、***children2.sh***。从***parent.sh***我正在导出一个SOME_VAR变量并同时执行./children1.sh & children2sh。然后在 * children1.sh * 和 * children2.sh * 中执行一些操作。此外,我们可以看到 * children2.sh * 正在覆盖值为20SOME_VAR变量。我希望将此更改反映在 * parent.sh * 中。例如,假设我有这样的脚本:

  • parent.sh*
#!/bin/bash
SOME_VAR=100
export SOME_VAR
bash ./children1.sh & ./children2.sh
echo "New SOME_VAR value: $SOME_VAR"

字符串

  • children1.sh*
#!/bin/bash
for i in {1..3}
do
   echo "### FIRST PROCESS  $i"
done

  • children2.sh*
#!/bin/bash
for i in {1..3}
do
   echo "### SECOND PROCESS  $i"
done
SOME_VAR=20


现在,如果我执行bash ./parent.sh,我会得到这样的输出:

### SECOND PROCESS  1 
### FIRST PROCESS  1 
### SECOND PROCESS  2 
### FIRST PROCESS  2 
### SECOND PROCESS  3 
### FIRST PROCESS  3 
New SOME_VAR value: 100


不幸的是,看起来***children2.sh操作没有反射回parent.sh***,并且SOME_VAR变量仍然等于100而不是20。我想出了一个主意,每个脚本都可以创建一个文件,并将其输出写入该文件。稍后,这些文件可以在子脚本完成其工作后由父脚本读取。这个解决方案对我来说听起来有点古怪。我不是bashMaven,所以也许有人可以帮助我提供更充分的解决方案。
有一个要求:您不能使用任何第三方库/工具,例如GNU Parallel

o2g1uqev

o2g1uqev1#

考虑到你在下面的评论中所说的,并不需要所有的脚本共享一个变量,而是只需要在后台运行的子脚本能够向父脚本返回一个值,像这样的东西可能是最好的:

$ cat parent.sh
#!/usr/bin/env bash

foo=1000
bar=999

run_children() {
    local -A children
    local -a pids
    local var

    children[foo]=./child1.sh
    children[bar]=./child2.sh

    for var in "${!children[@]}"; do
        printf '%s\n%s\0' "$var" "$( "${children[$var]}" )" &
        pids+=( $! )
    done

    wait "${pids[@]}"
}

while read -r -d '' var val; do
    printf -v "$var" '%s' "$val"
done < <( run_children )

printf 'foo=%s\n' "$foo"
printf 'bar=%s\n' "$bar"

字符串

$ cat child1.sh
#!/usr/bin/env bash

for i in {1..3}
do
   echo "### FIRST PROCESS  $i" >&2
   sleep 2
done

printf "now is the winter\nof our discontent\n"

$ cat child2.sh
#!/usr/bin/env bash

for i in {1..3}
do
   echo "### SECOND PROCESS  $i" >&2
   sleep 1
done

printf 'wee, sleekit, cowrin\ntimrous beastie\n'

$ ./parent.sh
### FIRST PROCESS  1
### SECOND PROCESS  1
### SECOND PROCESS  2
### FIRST PROCESS  2
### SECOND PROCESS  3
### FIRST PROCESS  3
foo=now is the winter
of our discontent
bar=wee, sleekit, cowrin
timrous beastie


我让孩子们输出多行字符串值,只是为了说明这种方法既适用于这种情况,也适用于所需的单个数字输出情况。
我给child1一个比child2更长的迭代间隔,以表明脚本在阅读它们的输出之前等待所有进程结束,并且不受它们运行顺序的影响。
请注意,在上述所有内容中,子脚本对父变量一无所知,因此保持了它们之间的松散耦合。如果任何子节点需要使用父节点中最初设置的foobar的值,除非有一些我们还不知道的原因,否则我建议将值作为参数传递以保持松散耦合,而不是将其导出到父节点中,然后在子节点中引用变量,因为这将不必要地产生不必要的紧耦合。
原始答案:
子进程不能更改父进程中变量的值。export让子进程看到变量的值,并在自己内部修改它,而不是修改父进程中的值。
您可以:

var=$( child )


将父级中的值设置为子级打印的字符串,或者:

child
var=$?


将父级中的值设置为从子级退出的整数状态,或

. child


使子进程在同一进程中运行,因此它的变量和父变量混合在一起,但这些都不会让您在后台运行的子进程之间共享变量-为此,您需要一个文件:

child1:
    echo 10 > "$TMP"

child2:
    echo 20 > "$TMP"

parent:
    export TMP=$(mktemp)
    echo 100 > "$TMP"
    child1 &
    child2 &
    sleep 5    # or "wait" or similar
    IFS= read -r val < "$TMP"
    echo "$val"


但是你可能应该在文件周围引入一些锁定机制(参见@CharlesDuffy在is-writing-to-a-unix-file-through-shell-script-is-synchronized和the flock man page上的回答),以防不同的进程试图同时读/写它,如果你对它所做的不是原子的话。
flock在大多数系统上都可用,但它不是POSIX,所以如果由于您的要求“您不能使用任何第三方库/工具”而无法使用它,那么显然您必须想出其他方法来完成这一部分。

相关问题