bash脚本-进程替换的变量扩展
我试图对多列文件的每一列执行分析,并使用“粘贴”将这些列重新连接在一起。我不知道有多少列,所以我使用“wc-w”和一个循环来定义一个命令数组。然后,每个命令都是一个进程替换。下面的脚本显示了我正在尝试的内容,之后将显示输出。值得注意的是,如果我将命令数组回送到终端,然后用鼠标剪切粘贴,效果很好,因此它必须是变量扩展和进程替换的顺序 简而言之,我需要在shell变量中有一个进程替换。有什么想法吗?提前谢谢 --------------script.sh----------------bash脚本-进程替换的变量扩展,bash,scripting,process,substitution,Bash,Scripting,Process,Substitution,我试图对多列文件的每一列执行分析,并使用“粘贴”将这些列重新连接在一起。我不知道有多少列,所以我使用“wc-w”和一个循环来定义一个命令数组。然后,每个命令都是一个进程替换。下面的脚本显示了我正在尝试的内容,之后将显示输出。值得注意的是,如果我将命令数组回送到终端,然后用鼠标剪切粘贴,效果很好,因此它必须是变量扩展和进程替换的顺序 简而言之,我需要在shell变量中有一个进程替换。有什么想法吗?提前谢谢 --------------script.sh---------------- $ ./sc
$ ./scipt.sh
File contents
A B C
D E F
G H I
First try
A B C G H I
D E F D E F
G H I A B C
Second try
A B C A B C
D E F D E F
G H I G H I
Third try
paste: <(cat: No such file or directory
Fourth try
paste: <(cat: No such file or directory
Show the array
paste <(cat file.txt) <(tac file.txt)
$ paste <(cat file.txt) <(tac file.txt)
A B C G H I
D E F D E F
G H I A B C
$
可能有一亿行这样的数据。我需要分离每一列,将每一列分离成(比如)1000个块,然后对块中的每个元素进行平均,然后将平均后的列再次合并在一起。对于下面的示例,如果我对每个3个元素的2个块进行平均(而不是每个元素的100K),那么第6列的输出将是:
0.0165 # =(0.027+0.006)/2 - 1st row from each size-3 block
0.042 # =(0.035+0.049)/2 - 2nd row
0.0515 # =(0.068+0.035)/2 - 3rd row
我已经有了程序来做这个平均(这是“一些复杂的分析”),而且效果很好。因此,我所需要做的就是让脚本分离列,将其输入到一些比较分析中,然后使用paste
将各种输出重新合并到列中。但是,这些文件是v。很大,我不知道有多少列。如果我知道只有两列,那么paste将“cmd1”和“cmd2”分别定义为单独的数组
$ cmd1=(cat $f)
$ cmd2=(tac $f)
$ paste <("${cmd1[@]}") <("${cmd2[@]}")
A B C G H I
D E F D E F
G H I A B C
试试这个:
{
cat -<<EOS
10 7.74336e-08 7.30689e-08 0.359106 19.981796 -0.160611 0.027
10 7.74336e-08 7.30689e-08 0.363938 19.985069 0.041319 0.035
10 7.74336e-08 7.30689e-08 0.363133 19.982094 0.041319 0.068
10 7.74336e-08 7.30689e-08 0.360716 19.981796 -0.160611 0.006
10 7.74336e-08 7.30689e-08 0.361522 19.981796 0.243249 0.049
10 7.74336e-08 7.30689e-08 0.357897 19.986260 0.041319 0.035
EOS
} |
awk '
BEGIN { binSz=3; binSzLim=binSz ; binSzLim++ }
NR==1{
# base error checking on number of cols in first record
maxCols=NF
maxColsLim=maxCols ; maxColsLim++
r=0
}
{
if (NF != maxCols) {
print "Skipping record, Mismatch in data, expected " maxCols ", found " NF " recs at " NR ":" $0
next
}
r++
#dbg print "r="r" NR=" NR ":$0=" $0
# load data into temp arr[] by column
for (c=1;c<maxColsLim;c++) {
arr[r,c]+=$c
#dbg printf ("arr["r","c"]=" arr[r,c] " " )
avgArr[r,c]++
#dbg print "avgArr["r","c"]="avgArr[r,c]
}
if(r>=binSz) {
r=0
}
}
END {
for (r=1;r<binSzLim;r++) {
#dbg print "r=" r " binSzLim=" binSzLim " " (r<binSzLim) "\t"
for (c=1;c<maxColsLim;c++) {
#dbg printf("arr["r","c"]=" arr[r,c] "\tavg=" arr[r,c]/binSz "\t")
printf( arr[r,c]/avgArr[r,c] " ")
}
printf "\n"
}
}
'
我在数据中添加了第一列10,以便于调试sum和avg是否正常工作
这是一个有趣的问题,感谢您的发帖,感谢您继续回答我的问题;-)
{cat-如果您的脚本仍然不起作用,这可能会有额外的帮助。仍然遵循glenn jackman的答案,但另外您可能希望在脚本内部执行此操作
set+o posix
从链接:
“进程替换不是POSIX兼容的功能,因此可能必须通过:set+o POSIX启用它”您需要使用tac
,还是只是为了“随机化(ish)”“你的数据?”如果你真的只需要并排获得一堆列,那么粘贴文件有什么问题?
?祝你好运。我只在示例中使用tac。我实际拥有的是类似于:cols=$(head-n 1$f | wc-w);例如((i=1;iis你有没有理由不使用awk或perl?花一个小时阅读,你会从一个全新的角度看到你的问题。如果你将帖子改为显示简化的示例输入和预期的输出,我们可能会帮助你。那么你已经有了“一些复杂的分析”的代码?祝你好运。我使用awk还不错,更好但是awk在这里对我来说似乎不合适。不幸的是,我不能用语言准确地解释为什么它看起来不合适。我已经更改了上面的帖子作为回复。所以,如果您的输入文件有100Mill行,您的最终输出将是100Mill/1000 over N列?(我今天不太想去,但如果你没有更好的解决方案,我会在有时间的时候尝试awk解决方案).祝您好运,对于好的样本数据,+1!谢谢,但我认为这并不能避免这样一个事实,即在最后一个命令中,我需要为100行以上的行祈祷一定数量的手指!如何在eval前面预挂time
呢?祝大家好运。谢谢,这似乎起到了作用,与bash一样,我不明白为什么。我本以为“”会取代$cmd[*]作为一个词;但显然不是。eval
似乎很关键。@hide我不太担心时间-这样每个复杂的分析都在自己的CPU内核上运行。有了awk,我认为它将在一个内核上运行…对吗?但我为您计时,在Intel I7 x4 HT上,6列数据花了4分钟es实时。感谢两位的帮助。我稍后将发布结论性回复并关闭线程。@fergalish,问题在于字符串中的shell语法。现在我终于理解了man bash
中的eval
部分,也就是说,它所做的只是连接和扩展参数。再次感谢。我今天晚些时候会继续讨论。干杯。#Sheller好的,我做了一个简短的分析。令人惊讶的是,awk程序使用的CPU时间更少。但是,原来的程序可以在并行CPU内核上运行,使用的实时性更低。这里:$time cat file | avg.awk>file.mean real 15m48.754s;user 16m30.090s;sys 0m13.100s$time../mean.sh file>file.mean real 3m12.871s;user 20m27.940s;sys 1m3.130s$
然而,在binSz=1M时,awk
的估计时间为1½小时,而最初的实时时间仅为4分钟。下一个问题:如何将相同的stdin
传输到各种流程替换中!请注意此空间:-)再次感谢。@fergalish:非常有趣。谢谢分享。你有,或者你能安装GNU并行吗?这可能会给你所需要的提升。祝你好运。我已经安装了GNU并行,现在使用它还有很多其他原因:-)感谢您让我注意到这一点。我现在修改了上面的最后一个脚本,对文件进行预处理,将文件分成列,进行平均,并将结果通过管道传输到单独的管道文件(mkpipe)。然后,粘贴程序将分别运行,并从所有管道读取输入并合并它们。唯一需要注意的是,所有管道都必须合理地同时写入,否则一个管道将填充并暂停输入到其他管道,所有操作都将停止。我要发布它吗?我打算建议将其发布到codereview.stackexchange.com,但不记得我是如何让其他S.E.相关网站使用我的openID的
$ cmd1=(cat $f)
$ cmd2=(tac $f)
$ paste <("${cmd1[@]}") <("${cmd2[@]}")
A B C G H I
D E F D E F
G H I A B C
cols=$(head -n 1 $f|wc -w)
for (( i=1 ; i<=cols ; i++ )); do
cmd[i]="<(cat $f|cut -f$i|Some_Complicated_Analysis)"
done
eval paste "${cmd[*]}" # quotes are important here
{
cat -<<EOS
10 7.74336e-08 7.30689e-08 0.359106 19.981796 -0.160611 0.027
10 7.74336e-08 7.30689e-08 0.363938 19.985069 0.041319 0.035
10 7.74336e-08 7.30689e-08 0.363133 19.982094 0.041319 0.068
10 7.74336e-08 7.30689e-08 0.360716 19.981796 -0.160611 0.006
10 7.74336e-08 7.30689e-08 0.361522 19.981796 0.243249 0.049
10 7.74336e-08 7.30689e-08 0.357897 19.986260 0.041319 0.035
EOS
} |
awk '
BEGIN { binSz=3; binSzLim=binSz ; binSzLim++ }
NR==1{
# base error checking on number of cols in first record
maxCols=NF
maxColsLim=maxCols ; maxColsLim++
r=0
}
{
if (NF != maxCols) {
print "Skipping record, Mismatch in data, expected " maxCols ", found " NF " recs at " NR ":" $0
next
}
r++
#dbg print "r="r" NR=" NR ":$0=" $0
# load data into temp arr[] by column
for (c=1;c<maxColsLim;c++) {
arr[r,c]+=$c
#dbg printf ("arr["r","c"]=" arr[r,c] " " )
avgArr[r,c]++
#dbg print "avgArr["r","c"]="avgArr[r,c]
}
if(r>=binSz) {
r=0
}
}
END {
for (r=1;r<binSzLim;r++) {
#dbg print "r=" r " binSzLim=" binSzLim " " (r<binSzLim) "\t"
for (c=1;c<maxColsLim;c++) {
#dbg printf("arr["r","c"]=" arr[r,c] "\tavg=" arr[r,c]/binSz "\t")
printf( arr[r,c]/avgArr[r,c] " ")
}
printf "\n"
}
}
'
10 7.74336e-08 7.30689e-08 0.359911 19.9818 -0.160611 0.0165
10 7.74336e-08 7.30689e-08 0.36273 19.9834 0.142284 0.042
10 7.74336e-08 7.30689e-08 0.360515 19.9842 0.041319 0.0515