您可以fork
简单地捕获 SIGCHLD 信号来计算 ed 进程。如果您可以编辑脚本文件,那么您可以这样做:
set -o monitor # or set -m
trap "((++fork))" CHLD
所以fork
变量将包含分叉的数量。最后你可以打印这个值:
echo $fork FORKS
对于 1000 行的输入文件,它将打印:
3000 FORKS
这段代码分叉有两个原因。一个用于每个expr ...
,一个用于`echo ...|grep...`
。所以在读取while循环中,fork
每次读取一行时;在处理while循环中它fork
是2次(一次是因为,expr ...
一次是为了`echo ...|grep ...`
)。所以对于一个 1000 行的文件,它会分叉 3000 次。
但这并不准确!这只是调用 shell 完成的分叉。有更多的分叉,因为`echo ...|grep...`
分叉启动一个bash来运行这段代码。但在它之后也是两次分叉:一次 forecho
和一次 for grep
。所以实际上是 3fork
秒,而不是 1 秒。所以它是 5000 个叉子,而不是 3000 个。
如果您还需要计算叉子的叉子(叉子......)(或者您无法修改 bash 脚本或您希望它从其他脚本中执行),则可以使用更精确的解决方案
strace -fo s.log ./x.sh
它将打印这样的行:
30934 execve("./x.sh", ["./x.sh"], [/* 61 vars */]) = 0
然后您需要使用类似这样的方法计算唯一的 PID(第一个数字是 PID):
awk '{n[$1]}END{print length(n)}' s.log
如果是我得到的这个脚本5001
(+1 是原始bash脚本的 PID)。
评论
实际上在这种情况下,所有fork
s 都可以避免:
代替
cnt=`expr $cnt + 1`
采用
((++cnt))
代替
matches=`echo ${lineArray[$cnt]}|grep $match`
if [ "$matches" ] ; then
echo ${lineArray[$cnt]}
fi
您可以使用bash的内部模式匹配:
[[ ${lineArray[cnt]} =~ $match ]] && echo ${lineArray[cnt]}
请注意bash =~
使用 ERE 而不是 RE(如 grep)。所以它的行为就像egrep(或grep -E
),而不是grep。
我假设定义lineArray
不是毫无意义的(否则在阅读循环中可以测试匹配并且lineArray
不需要)并且它也用于其他目的。在这种情况下,我可能会建议一个更短的版本:
readarray -t lineArray <infile
for line in "${lineArray[@]}";{ [[ $line} =~ $match ]] && echo $line; }
第一行读取完整infile
的 tolineArray
没有任何循环。第二行是逐个元素处理数组。
措施
1000 行的原始脚本(在cygwin上):
$ time ./test.sh
3000 FORKS
real 0m48.725s
user 0m14.107s
sys 0m30.659s
修改版
FORKS
real 0m0.075s
user 0m0.031s
sys 0m0.031s
在linux上也一样:
3000 FORKS
real 0m4.745s
user 0m1.015s
sys 0m4.396s
和
FORKS
real 0m0.028s
user 0m0.022s
sys 0m0.005s
所以这个版本根本不使用fork
(或clone
)。我可能建议将此版本仅用于小(<100 KiB)文件。在其他情况下, grap、egrep、awk过度执行纯bash解决方案。但这应该通过性能测试来检查。
对于linux上的一千行,我得到以下信息:
$ time grep Solaris infile # Solaris is not in the infile
real 0m0.001s
user 0m0.000s
sys 0m0.001s