Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

sh/bash/dash/ksh/zsh等Shell脚本
回复
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#1

帖子 xc79522 » 2014-11-18 4:52

刚刚开始接触bash script和使用gnuplot作图,下面是我面临的任务,请教大家,如何写脚本,满足下面的任务要求:

一共有2个txt数据文件,暂且称文件1为:exp.txt(实验数据),文件2为sim.txt(模拟结果)。

文件1:exp.txt里的内容、及格式化大致如下:

x-axis species5_mole
1.000 0.3
2.000 0.4
3.000 0.5
4.000 0.6
......... .....

x-axis species8_mole
1.000 0.2
2.000 0.3
3.000 0.35
4.000 0.4
......... .....

即:每一个data block里的第一行是x、y轴的label,label之间是一个空格分隔;从每个data block的第2行起,就是x、y轴各自的value,value之间也是一个空格分隔;每个data block之间由一个空行分隔;data block的个数会变,比如有时是10个,有时是15个,但都是预先给定好的。

文件2:sim.txt里的内容、及格式化大致如下:

x-axis species1 species2 species3 ... species100
0.00000000 1.15174695E-014 5.28281175E-001 2.28199430E-004 ... 2.28199430E-004
1.60281966 1.14743622E-014 5.28276072E-001 2.28322770E-004 ... 2.28199430E-004
3.28277311 1.08184960E-014 5.28270725E-001 2.28452003E-004 ... 2.28199430E-004
4.00000000 6.84286765E-015 5.28232698E-001 2.29371009E-004 ... 2.28199430E-004
.................. ............................ ........................... ............................. ... ............................

即:第1列是x轴,之后的列都是各个species的value;第一行的文字、及从第2行起的数值,彼此间都是由一个空格分隔;但文件2里的列数(比如100列)远远超过文件1里的data block数目。

我需要得到文件3:sim_reduce.txt,该文件里的内容、及格式化应该如下:

x-axis species5 species8 species11 species13 species14 ...
value value value value value value …
value value value value value value …
value value value value value value …
value value value value value value …

即:该文件3里只有那些在文件1里出现的species的模拟数据,也就是说,文件1里有N个data block,文件3里就有(N+1)个列(因为第1列是x轴);而且顺序相对应,就是说,对应文件1里第X个data block的模拟数据,应该位于文件3里的第(X+1)列。

一下子写了这么多,不知我是否表达清楚了。非常感谢,如果能行的话,将大大简化我的重复行劳动。
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#2

帖子 astolia » 2014-11-18 9:22

文件2和生成的文件3有什么关系?似乎只需要文件1就行了
文件1中每个数据块的第一列是否都相同?
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#3

帖子 eexpress » 2014-11-18 11:41

没看出相同的行。。。
● 鸣学
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#4

帖子 xc79522 » 2014-11-18 16:01

astolia 写了:文件2和生成的文件3有什么关系?似乎只需要文件1就行了
文件1中每个数据块的第一列是否都相同?
非常谢谢快速的回复。

文件2与生成的文件3是包含与被包含关系,文件2包含文件3,因为文件2里有很多列是我不需要的(文件1、2都是原始data,我不想改动它们),而在文件3里所有的列都是我需要的。

至于文件3里有哪些列出现,是由文件1中每个数据块的第一行的那个speciesN_mole决定(N比如等于3、5、7等)。

文件1中每个数据块的第一列都相同。
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#5

帖子 xc79522 » 2014-11-18 16:01

eexpress 写了:没看出相同的行。。。
非常谢谢快速的回复。

抱歉,可能我没说清楚:文件3里的那个value,其实都是数值(就像文件2里一样),只不过我省事起见写成了文字“value”。
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#6

帖子 astolia » 2014-11-18 20:58

这样?

代码: 全选

$ cat sim.txt 
x-axis species1 species5 species8 species100
0.00000000 1.15174695E-014 5.28281175E-001 2.28199430E-004 2.28199430E-004
1.60281966 1.14743622E-014 5.28276072E-001 2.28322770E-004 2.28199430E-004
3.28277311 1.08184960E-014 5.28270725E-001 2.28452003E-004 2.28199430E-004
4.00000000 6.84286765E-015 5.28232698E-001 2.29371009E-004 2.28199430E-004
$ 
$ cat exp.txt 
x-axis species5_mole
1.000 0.3
2.000 0.4
3.000 0.5
4.000 0.6

x-axis species8_mole
1.000 0.2
2.000 0.3
3.000 0.35
4.000 0.4
$ 
$ COLS="$(head -1 sim.txt | COLS="$(grep '^x-axis' exp.txt | sed 's/^x-axis //;s/_mole$//')" awk 'BEGIN{split(ENVIRON["COLS"],arr,/\n/)}{for(i=1;i<=NF;i++){for(j in arr){if($i==arr[j]){print i}}}}')" awk 'BEGIN{ORS="";split(ENVIRON["COLS"],arr,/\n/)}{print $1;for(i in arr){print " " $arr[i]}print "\n"}' sim.txt
x-axis species5 species8
0.00000000 5.28281175E-001 2.28199430E-004
1.60281966 5.28276072E-001 2.28322770E-004
3.28277311 5.28270725E-001 2.28452003E-004
4.00000000 5.28232698E-001 2.29371009E-004
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#7

帖子 xc79522 » 2014-11-20 6:20

astolia 写了:这样?

代码: 全选

$ cat sim.txt 
x-axis species1 species5 species8 species100
0.00000000 1.15174695E-014 5.28281175E-001 2.28199430E-004 2.28199430E-004
1.60281966 1.14743622E-014 5.28276072E-001 2.28322770E-004 2.28199430E-004
3.28277311 1.08184960E-014 5.28270725E-001 2.28452003E-004 2.28199430E-004
4.00000000 6.84286765E-015 5.28232698E-001 2.29371009E-004 2.28199430E-004
$ 
$ cat exp.txt 
x-axis species5_mole
1.000 0.3
2.000 0.4
3.000 0.5
4.000 0.6

x-axis species8_mole
1.000 0.2
2.000 0.3
3.000 0.35
4.000 0.4
$ 
$ COLS="$(head -1 sim.txt | COLS="$(grep '^x-axis' exp.txt | sed 's/^x-axis //;s/_mole$//')" awk 'BEGIN{split(ENVIRON["COLS"],arr,/\n/)}{for(i=1;i<=NF;i++){for(j in arr){if($i==arr[j]){print i}}}}')" awk 'BEGIN{ORS="";split(ENVIRON["COLS"],arr,/\n/)}{print $1;for(i in arr){print " " $arr[i]}print "\n"}' sim.txt
x-axis species5 species8
0.00000000 5.28281175E-001 2.28199430E-004
1.60281966 5.28276072E-001 2.28322770E-004
3.28277311 5.28270725E-001 2.28452003E-004
4.00000000 5.28232698E-001 2.29371009E-004
非常感谢,针对上面这个例子(sim.txt、exp.txt)确实能用!

请教astolia:
上面这个例子(sim.txt、exp.txt)里的数据\格式,都是我为了举例而给的假想出来的。为什么我使用同样的命令对我下面实际的例子,就不行了呢?

实际例子:

cat exp.txt
hab[mm] A1_molefraction[-]
0.42587 0.11844
3.12303 0.105674
4.40063 0.0801418
5.11041 0.058156

hab[mm] A1C2H_molefraction[-]
3.26519 4.94373e-05
4.94664 0.000304553
6.07465 0.000609801
7.07754 0.000347735

hab[mm] A2_molefraction[-]
3.26968 3.55234e-05
4.9572 0.000140069
5.80174 0.000262505
6.94614 0.000157304

cat sim.txt
x[mm] C AR A1 A1- A1C2H A1C2H-M
0.00000000E+000 4.03712310E-016 3.82186262E-001 9.16676140E-002 2.03271992E-006 1.83264579E-004 2.43353082E-008
1.06128907E-004 4.03727668E-016 3.82183696E-001 9.16648357E-002 2.03297647E-006 1.83291555E-004 2.43388851E-008
2.16313406E-004 4.03768634E-016 3.82181032E-001 9.16619510E-002 2.03324936E-006 1.83319566E-004 2.43426214E-008
3.33182039E-004 4.03839828E-016 3.82178206E-001 9.16588910E-002 2.03354608E-006 1.83349280E-004 2.43466096E-008

文件3里应该只有3列:
x[mm] A1 A1C2H

我使用的命令是:
COLS="$(head -1 sim.txt | COLS="$(grep '^hab' exp.txt | sed 's/^hab[mm] //;s/_molefraction[-]$//')" awk 'BEGIN{split(ENVIRON["COLS"],arr,/\n/)}{for(i=1;i<=NF;i++){for(j in arr){if($i==arr[j]){print i}}}}')" awk 'BEGIN{ORS="";split(ENVIRON["COLS"],arr,/\n/)}{print $1;for(i in arr){print " " $arr}print "\n"}' sim.txt

我目前唯一能想到的不成功的可能原因是:是不是因为sim.txt、exp.txt里每一行的数据之间有的是tab分隔,有的是空格分隔?
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#8

帖子 astolia » 2014-11-20 9:52

你把整个命令拆分出来看,除去变量赋值传递部分,有这么三个主要步骤
grep '^hab' exp.txt | sed 's/^hab[mm] //;s/_molefraction[-]$//'
head -1 sim.txt | awk 'BEGIN{split(ENVIRON["COLS"],arr,/\n/)}{for(i=1;i<=NF;i++){for(j in arr){if($i==arr[j]){print i}}}}'
awk 'BEGIN{ORS="";split(ENVIRON["COLS"],arr,/\n/)}{print $1;for(i in arr){print " " $arr}print "\n"}' sim.txt

第一步是从exp.txt中提取出列名
第二步是从sim.txt的第一行中,将第一步提取出列名转换成对应的序号
第三步是对sim.txt的每一行,只输出第二步中得到的列序号

你的问题出在两方面,一是第一步中对列名的提取,二是你所说的列的分隔
因为sed用的是正则表达式,[和]在正则表达式中有特殊的含义,你的情况下需要转义。也就是换成\[和\]
列分隔的话,需要给二三步中的awk加个参数。用awk -F '(\t| )+',表示列分隔符是至少一个tab或空格。如果确定没有连续的tab或空格作分隔的话,可以去掉+号。这也是个正则表达式,如果对正则表达式不熟悉,可以找找看那篇正则表达式30分钟入门教程
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#9

帖子 xc79522 » 2014-11-20 17:23

非常感谢astolia!学习了!试了试,确实可行!

我只还有一个小问题:
如何调整第二、三步(我估计应该是调整那个for循环吧),才能使文件3里的列的顺序和exp.txt里的数据块的顺序一致,即:文件3里第一列是x[mm],第二列是A1,第三列是A1C2H……?(因为exp.txt的实际情况是里面包含30多块数据块)
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#10

帖子 astolia » 2014-11-20 23:54

把第二步两个for循环对调一下就行了
xc79522
帖子: 6
注册时间: 2014-11-18 4:38
系统: win7

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#11

帖子 xc79522 » 2014-11-21 23:39

谢谢。如果是下面这样对调的话:

COLS="$(head -1 sim.txt | COLS="$(grep '^hab' exp.txt | sed 's/^hab\[mm\] //;s/_molefraction\[-\]$//')" awk -F '(\t| )+' 'BEGIN{split(ENVIRON["COLS"],arr,/\n/)}{for(j in arr){for(i=1;i<=NF;i++){if($i==arr[j]){print i}}}}')" awk -F '(\t| )+' 'BEGIN{ORS="";split(ENVIRON["COLS"],arr,/\n/)}{print $1;for(i in arr){print " " $arr}print "\n"}' sim.txt

我试了试,似乎还是不行呀。
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Shell脚本求助:2个文本之间搜索匹配列,并输出结果到第3个文件

#12

帖子 astolia » 2014-11-23 18:59

我这边是没问题的,你那边的输出是啥?你又想得到什么输出?
回复