在后台运行shell函数(使用&)但仍然检索结果

时间:2016-01-22 22:21:27

标签: bash

是否可以使用'&'调用BASH功能并返回一个值?或者,还有更好的方法?基本上我想并行运行相同的命令几次并捕获数据。

在这个示例无意义的代码中,我想从函数中获取值,但它没有被赋值。如果我能做到这一点,那么我可以调用一个函数并将其并行传递给它。

npm install

1 个答案:

答案 0 :(得分:2)

这可以使用bash 4.x和coprocesses来完成:

#!/bin/bash
# REQUIRES BASH 4.1+

pbas() {
  sleep 10
  printf '%s\0' "hello, world: $RANDOM"
}

echo "Starting coprocesses $SECONDS seconds after interpreter startup"

# Start three coprocesses
coproc cp1 { pbas; }
coproc cp2 { pbas; }
coproc cp3 { pbas; }

# Read results from each in turn
IFS= read -r -d '' result1 <&${cp1[0]}
IFS= read -r -d '' result2 <&${cp2[0]}
IFS= read -r -d '' result3 <&${cp3[0]}

# Emit said results
echo "Result 1: $result1"
echo "Result 2: $result2"
echo "Result 3: $result3"

echo "Exiting $SECONDS seconds after interpreter startup"

你会发现它在解释器启动后大约10秒内正确退出,而不是如果函数的每个实例在父shell中运行就会得到30。

现在,如果你想变得有点棘手(并且,对于这个例子,依赖bash 4.3),你可以做更多有趣的事情 - 减少重复代码的数量:

#!/bin/bash
# REQUIRES BASH 4.3+

pbas() {
  sleep 10
  printf '%s\0' "hello, world: $RANDOM"
}

declare -A all_coprocs=( )
start_coproc() {
  local retval_name=$1; shift
  local eval_header eval_str eval_footer
  printf -v eval_header 'coproc cp_%q { ' "$retval_name"
  printf -v eval_str '%q ' "$@"
  printf -v eval_footer '; }'
  eval "${eval_header}${eval_str}${eval_footer}"
  all_coprocs[$retval_name]="cp_${retval_name}"
}

collect_results() {
  local retval_name
  for retval_name in "${!all_coprocs[@]}"; do
    declare -n cp=${all_coprocs[$retval_name]}
    IFS= read -r -d '' "$retval_name" <&${cp[0]}
    unset -n cp
  done
}

start_coproc result1 pbas
start_coproc result2 pbas
start_coproc result3 pbas
collect_results

echo "Result 1: $result1"
echo "Result 2: $result2"
echo "Result 3: $result3"

echo "Exiting $SECONDS seconds after interpreter startup"

现在,如果你只有bash 3.x,那么事情会变得更棘手:你没有自动FD分配,或协同处理,或支持从变量命名的文件描述符重定向[除了使用eval ]。但是,您可以生成命名管道;产生写入它们的子进程;然后在收集阶段从他们那里读取。

#!/bin/bash
# REQUIRES BASH 3.2+

declare -a destvars=( )
declare -a destdirs=( )
declare -a destfds=( )
nextfd=10

pbas() { sleep 10; printf '%s\0' "hello, world: $RANDOM"; }

start_proc() {
  local destvar=$1; shift
  local destdir=$(mktemp -d "${TEMPDIR:-/tmp}/coproc-emulation.XXXXXX")
  local idx=$(( ${#destdirs[@]} + 1 ))
  local eval_str
  mkfifo "$destdir/outpipe"
  ( "$@" ) >"$destdir/outpipe" & procs[$idx]=$!
  printf -v eval_str 'exec %q<%q' "$nextfd" "$destdir/outpipe"
  eval "$eval_str"
  destdirs[$idx]=$destdir
  destvars[$idx]=$destvar
  destfds[$idx]=$((nextfd++))
}

collect_results() {
  local idx destdir destvar destfd
  for idx in "${!destvars[@]}"; do
    destvar=${destvars[$idx]}
    destdir=${destdirs[$idx]}
    destfd=${destfds[$idx]}
    printf -v eval_str 'IFS= read -r -d "" %q <&%q' "$destvar" "$destfd"
    eval "$eval_str"
    rm -rf "$destdir"
    unset destvars[$idx] destdirs[$idx] destfds[$idx]
  done
}

start_proc result1 pbas
start_proc result2 pbas
start_proc result3 pbas

collect_results

echo "Result 1: $result1"
echo "Result 2: $result2"
echo "Result 3: $result3"

echo "Exiting $SECONDS seconds after interpreter startup"