我看了一下(ab?)使用大括号扩展的想法:
p='{A,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y}'
eval echo $p$p$p$p$p$p$p
在 7 的一个简单步骤中使用这种直接方法$p
对于 bash 来说太过分了。没有明显的原因,它吃掉了所有的内存(随时间的测量显示没有其他内存值增长得如此之快)。
该命令非常快速且非常简单,最多大约 4$p
行,只有两行:
p='{A,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y}'
eval echo $p$p$p$p
但是,内存使用量增长很快。在 6$p
次重复的深度,该过程消耗超过 7.80 Gigs 的内存。eval 部分还有助于增加执行时间和内存使用量。
需要一种替代方法。因此,我尝试利用 Jonathan Leffler 使用的概念,自行完成扩展的每一步。对于输入中的每一行,写 19 行,每行在输出中附加一个字母。我发现任何 eval 都是重要的内存消耗(此处未显示)。
重击
一个更简单的 bash 过滤器是:
bashfilter(){
while read -r line; do
printf '%s\n' ${line}{A,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y}
done </dev/stdin
}
可用于多个处理级别:
echo | bashfilter | bashfilter | bashfilter
它只需要重复尽可能多的过滤步骤,就像每行需要的字母一样。
使用这种更简单的方法:内存不再是问题。然而,速度变得更糟。
莱弗勒 SED
只是为了比较,用它作为量尺,我实现了莱弗勒的想法:
# Building Leffler solution:
leftext="$(<<<"${list}" sed -e 's/,/\n/g')" # list into a column.
leftext="$(<<<"${leftext}" sed -e 's%.%s/$/&/p;s/&$//%')" # each line ==> s/$/?/p;s/?$//
# echo -e "This is the leffilter \n$leftext"
leffilter(){ sed -ne "$leftext"; } # Define a function for easy use.
并且是可以递归使用的 leffilter 以获得每行所需的尽可能多的字母:
echo | leffilter | leffilter | leffilter
Leffler 解决方案做了一个字母插入和一个字母擦除。
SED
无需擦除一个字母就可以减少工作量。我们可以将原始模式空间存储在“保持空间”中。
然后,只需将第一行复制到保留空间 (h),然后继续恢复它 (g) 并仅插入一个字母。
# Building a sed solution:
sedtext="$(<<<"${list}" sed -e 's/,/\n/g')" # list into a column.
sedtext="$(<<<"${sedtext}" sed -e 's%[A-Z]%g;s/$/&/p;%g')" # s/$/?/p
sedtext="$(<<<"${sedtext}" sed -e '1 s/g/h/' )" # 1st is h
sedfilter(){ sed -ne "$sedtext"; } # Define a function for easy use.
这样做可以提高速度,降低约 1/3 (33%)。或快 1.47 倍。
AWK
最后,我提出一个 AWK 解决方案。我之前写过,但是是最快的。所以我把它作为最后的选择。最好的,直到有人提出更好的:-)
# An AWK based solution:
awkfilter(){ awk 'BEGIN { split( "'"$list"'",l,",");}
{ for (i in l) print $0 l[i] }'
}
是的,只有两行。它的速度是 Leffler 解决方案的一半或两倍。
使用的完整测试脚本如下。它重新调用自身以启用外部时间的使用。确保它是带有 bash 的可执行文件。
#!/bin/bash
TIMEFORMAT='%3lR %3lU %3lS'
list="A,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y"
# A pure bash based solution:
bashfilter(){
while read -r line; do
printf '%s\n' ${line}{A,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y}
done </dev/stdin
}
# Building Leffler solution:
leftext="$(<<<"${list}" sed -e 's/,/\n/g')" # list into a column.
leftext="$(<<<"${leftext}" sed -e 's%.%s/$/&/p;s/&$//%')" # each line ==> s/$/?/p;s/?$//
# echo -e "This is the lef filter \n$leftext"
leffilter(){ sed -ne "$leftext"; } # Define a function for easy use.
# Building a sed solution:
sedtext="$(<<<"${list}" sed -e 's/,/\n/g')" # list into a column.
sedtext="$(<<<"${sedtext}" sed -e 's%[A-Z]%g;s/$/&/p;%g')" # each letter ==> s/$/?/p
sedtext="$(<<<"${sedtext}" sed -e '1 s/g/h/' )" # First command is 'h'.
# echo -e "This is the sed filter \n$sedtext"
sedfilter(){ sed -ne "$sedtext"; } # Define a function for easy use.
# An AWK based solution:
awkfilter(){ awk 'BEGIN { split( "'"$list"'",l,",");}
{ for (i in l) print $0 l[i] }'
}
# Execute command filter
docommand(){
local a count="$1" filter="$2" peptfile="$3"
for (( i=0; i<count; i++ )); do
case $filter in
firsttry) a+=("{$list}"); ;;
*) a+=("| $filter"); ;;
esac
done
[[ $filter == firsttry ]] && a+=('| sed '"'"'s/ /\n/'"'" )
[[ -n $peptfile ]] && peptfile="$peptfile.$count"
eval 'echo '"$(printf '%s' "${a[@]}")" > "${peptfile:-/dev/null}";
}
callcmd(){
tf='wall:%e s:%S u:%U (%Xtext+%Ddata %F %p %t %Kmem %Mmax)'
printf '%-12.12s' "$1" >&2
/usr/bin/time -f "$tf" "$0" "$repeats" "$1" "$2"
}
nofile=1
if (( $#>=2 )); then
docommand "$1" "$2" "$3"; exit 0
else
for (( i=1; i<=6; i++)); do
repeats=$i; echo "repeats done = $repeats"
if ((nofile)); then
callcmd firsttry
callcmd bashfilter
callcmd leffilter
callcmd sedfilter
callcmd awkfilter
else
callcmd firsttry peptidesF
callcmd bashfilter peptidesB
callcmd leffilter peptidesL
callcmd sedfilter peptidesS
callcmd awkfilter peptidesA
fi
done
fi
结果
使用外部程序 /usr/bin/time(而不是 bash 内置时间)来测量所使用的内存。在这个问题中很重要。
With: tf='wall:%es:%S u:%U (%Xtext+%Ddata %F %p %t %Kmem %Mmax)'
使用上面的脚本很容易找到 7 个循环和真实文件输出的结果,但我觉得填充大约 21 GB 的磁盘空间实在是太多了。
最多 6 个循环的结果是:
repeats done = 1
firsttry wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
bashfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1552max)
leffilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
sedfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
awkfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1560max)
:
repeats done = 2
firsttry wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
bashfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1552max)
leffilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1560max)
sedfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
awkfilter wall:0.01 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1560max)
:
repeats done = 3
firsttry wall:0.02 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1796max)
bashfilter wall:0.07 s:0.00 u:0.05 (0text+0data 0 0 0 0mem 1552max)
leffilter wall:0.02 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
sedfilter wall:0.02 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1560max)
awkfilter wall:0.02 s:0.00 u:0.00 (0text+0data 0 0 0 0mem 1556max)
:
repeats done = 4
firsttry wall:0.28 s:0.01 u:0.26 (0text+0data 0 0 0 0mem 25268max)
bashfilter wall:0.96 s:0.03 u:0.94 (0text+0data 0 0 0 0mem 1552max)
leffilter wall:0.13 s:0.00 u:0.12 (0text+0data 0 0 0 0mem 1560max)
sedfilter wall:0.10 s:0.00 u:0.08 (0text+0data 0 0 0 0mem 1560max)
awkfilter wall:0.09 s:0.00 u:0.07 (0text+0data 0 0 0 0mem 1560max)
:
repeats done = 5
firsttry wall:4.98 s:0.36 u:4.76 (0text+0data 0 0 0 0mem 465100max)
bashfilter wall:20.19 s:0.81 u:20.18 (0text+0data 0 0 0 0mem 1552max)
leffilter wall:2.43 s:0.00 u:2.50 (0text+0data 0 0 0 0mem 1556max)
sedfilter wall:1.83 s:0.01 u:1.87 (0text+0data 0 0 0 0mem 1556max)
awkfilter wall:1.49 s:0.00 u:1.54 (0text+0data 0 0 0 0mem 1560max)
:
repeats done = 6
firsttry wall:893.06 s:30.04 u:105.22 (0text+0data 402288 0 0 0mem 7802372m)
bashfilter wall:365.13 s:14.95 u:368.09 (0text+0data 0 0 0 0mem 1548max)
leffilter wall:51.90 s:0.09 u:53.91 (0text+0data 6 0 0 0mem 1560max)
sedfilter wall:35.17 s:0.08 u:36.67 (0text+0data 0 0 0 0mem 1556max)
awkfilter wall:25.60 s:0.06 u:26.77 (0text+0data 1 0 0 0mem 1556max)