我已经读过这样的文章:如何在Bash中将"find"命令结果存储为数组,或者从Bash中的文本文件创建数组,或者将命令输出存储到数组中
现在我的问题是:如何在并行中做到这一点?
背景:
我有一个脚本,用于处理一个包含许多子模块的大型git存储库,并在其中执行某些操作。有时有些任务需要一段时间,所以与此同时,我想给用户一些反馈,以表明某些事情仍在发生,代码不仅仅停留在^^
我有一个功能
function ShowSpinner()
{
pid=$!
while [ -d /proc/$pid ]
do
for x in '-' '/' '|' '\'
do
echo -ne ${x}" r"
sleep 0.1
done
done
}
用于在执行长任务时显示小微调器。到目前为止,我使用这个,例如
while IFS= read -r line
do
# Some further processing of the output lines here
done <<< $(git pull 2>&1) & ShowSpinner
其工作良好并且总是显示微调器直到任务完成。
特别是,我还使用它来查找像这样的git存储库中的子模块
function FindSubmodules()
{
# find all .git FILES and write the result to the temporary file .submodules
find -name ".git" -type f > .submodules & ShowSpinner
# read in the temporary file
SUBMODULES=$(cat .submodules)
# and delete the temporary file
rm .submodules
}
稍后,我使用例如迭代子模块
function DoSomethingWith()
{
for submodule in ${SUBMODULES}
do
echo $submodule
done
}
FindSubmodules
DoSomethingWith
当然,我在那里做了更多的事情,这只是一个简短的例子。
这是可行的,但我不喜欢这里创建的文件.submodules
(如果只是临时的(。我更喜欢直接将结果存储在数组中,然后直接迭代该数组。
所以在阅读了提到的帖子后,我试着使用类似的东西
IFS=$'n'
SUBMODULES=( $(find -name ".git" -type f)) & ShowSpinner
或从链路也
readarray SUBMODULES < <(find -name ".git" -type f) & ShowSpinner
或
readarray -t SUBMODULES "$(find -name ".git" -type f)" & ShowSpinner
然后像一样迭代
for submodule in ${SUBMODULES [@]}
do
echo $submodule
done
对于所有三个选项,结果基本相同:微调器运行良好,但我使用它得到的只是一个带有ShowSpinner
最后一个字符的条目,而不是find
的结果。如果没有& ShowSpinner
,它可以很好地工作,但当然不会显示出任何长任务的反馈。
我做错了什么?如何使readarray
与ShowSpinner
函数并行工作?
更新按照建议我已经把它放在了一个函数中(实际上我已经有了函数,只是到目前为止没有把微调器放在整个函数后面(
function FindSubmodules()
{
echo ""
echo ${BOLD}"Scanning for Submodules ... "${NORMAL}
SUBMODULES=($(find -name ".git" -type f))
for submodule in "${SUBMODULES[@]}"
do
echo $submodule
done
}
function CheckAllReposForChanges()
{
# Check Submodules first
for submodule in "${SUBMODULES[@]}"
do
# remove prefixed '.'
local removedPrefix=${submodule#.}
# remove suffix '.git'
local removedSuffix=${removedPrefix%.git}
echo "${BASEPATH}${removedSuffix}"
done
# Check the main repo itself
echo "${BASEPATH}"
echo ""
}
FindSubmodules & ShowSpinner
CheckAllReposForChanges
CCD_ 7函数本身工作得很好。
我现在得到的是微调器,然后是第一个FindSubmodules
的正确输出,比如
./SomeFolder/.git
./SomeOtherFolder/.git
./SomeThirdFolder/.git
etc
然而,当谈到CheckAllReposForChanges
时(同样,echo
只是调试的一个示例(,除了主存储库路径之外,我没有得到任何输出。现在SUBMODULES
似乎是空的,因为它是在后台填充的。它适用于我最初使用的解决方案。
当然,数组是空的;您已经对最终将填充它的函数进行了后台处理(无论如何,一旦变量完成,后台进程就无法在其父级中填充它(。
在同一个后台进程中运行这两个函数,它们将能够正确地进行通信。
{ FindSubmodules
CheckAllReposForChanges
} & ShowSpinner
也许我误解了这个问题,但(对我来说(要求是将数据从后台/子进程"向上"传递到调用/父进程。
后台脚本/函数调用生成一个新的异步操作系统级进程;没有简单的方法可以将数据从子进程"向上"传递到父进程。
虽然可以建立某种进程间共享内存结构来在父进程和子进程之间共享数据,但如果我们可以使用各种进程可以"共享"的某种中间存储(例如,fifo、文件、数据库表、排队系统等(,则会更容易一些。
一个想法:
- 父进程创建一个或多个临时目录(例如,每个要填充的不同数组一个(
- 每个子进程以父进程可以轻松解析(并加载到数组中(的格式将数据写入特定临时目录中的文件(文件名=
${BASHPID}
( - 父进程调用子进程,等待子进程完成,然后
- 父进程读取临时目录中所有文件的内容并加载适当的数组
为了举例,我假设我们只需要填充一个数组;我还将使用相同的临时目录来捕获/存储函数的模块,无论每个函数是在后台还是前台运行:
unset submodules # delete any variable with this name
submodules=() # init array
outdir=$(mktemp -d) # create temp directory
FindSubmodules()
{
... snip ...
echo "$submodule" >> "${outdir}/${BASHPID}" # write module to temp file
... snip ...
}
CheckAllReposForChanges()
{
... snip ...
echo "$submodule" >> "${outdir}/${BASHPID}" # write module to temp file
... snip ...
}
FindSubmodules & ShowSpinner
CheckAllReposForChanges
# now pull modules from temp file(s) into array; NOTE: assumes each temp file contains a single module name on each line, and no blank lines, otherwise OP can add some logic to address a different format
while read -r modname
do
submodules+=(${modname})
done < <(cat "${outdir}"/[0-9]*)
# remove temp directory and file(s)
'rm' -rf ${outdir}
如果您可以使用GNU并行程序编写并行程序,那么您可以使用parset
:
dostuff() {
# Do real stuff here
sleep 10;
}
export -f dostuff
function ShowSpinner()
{
while [ -d /proc/$pid ]
do
for x in '-' '/' '|' '\'
do
echo -ne ${x}" r"
sleep 0.1
done
done
}
sleep 1000000 &
pid=$!
ShowSpinner &
parset myout dostuff < <(find -name ".git" -type f)
kill $pid
echo
或者(如果您愿意更改ShowSpinner:
dostuff() {
# Do real stuff here
sleep 10;
}
export -f dostuff
function ShowSpinner()
{
while true; do
for x in '-' '/' '|' '\'
do
echo -ne ${x}" r"
sleep 0.1
done
done
}
ShowSpinner &
pid=$!
parset myout dostuff < <(find -name ".git" -type f)
kill $pid
echo