是否可以使用'&'调用BASH功能并返回一个值?或者,还有更好的方法?基本上我想并行运行相同的命令几次并捕获数据。
在这个示例无意义的代码中,我想从函数中获取值,但它没有被赋值。如果我能做到这一点,那么我可以调用一个函数并将其并行传递给它。
npm install
答案 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"