[英]Concatenate every four files, Linux
我有两个文件存储文件名列表:
FileA:
GSM1328513
GSM1328514
GSM1328515
GSM1328516
GSM1328545
GSM1328546
GSM1328547
GSM1328548
GSM1328609
GSM1328610
GSM1328611
GSM1328612
and:
FileB:
Brn
Hrt
Lng
我想要做的是,连接fileA中列出的每四个文件,并将连接文件命名为fileB中列出的文件名:要手动执行,它看起来像:
cat GSM1328513 GSM1328514 GSM1328515 GSM1328516 > Brn
cat GSM1328545 GSM1328546 GSM1328547 GSM1328548 > Hrt
cat GSM1328609 GSM1328610 GSM1328611 GSM1328612 > Lng
由于我有很长的文件列表,我想自动完成,任何人都可以提供帮助。 如果有任何不清楚的地方,请指出。
另一个没有sed
快速方法:
cat FileA | while read a ; do read b ; read c ; read d ;
echo "cat $a $b $c $d > " ; done | paste - FileB | bash
正如Didier Trosset所说,你可以跳过| bash
| bash
在执行它之前看看它做了什么。
其他方法:没有评估的单线程,将@dshepherd解决方案与我的相结合:
cat FileA | xargs -n4 echo | paste - FileB | while read a b c d e ; do cat $a $b $c $d > $e ; done
优点:到目前为止,这是唯一一个不评估任何输出( | bash
)并且不使用临时文件的单行程序,并且只使用在任何地方找到的标准工具( cat
, xargs
, paste
)。
以下是执行您要执行的操作的Shell脚本
iter=0
while read filename
do
stop=`expr \( $iter + 1 \) \* 4`
iter=`expr $iter + 1`
files=`head -n $stop fileA | tail -n 4 | tr '\n' ' '`
cat $files > $filename
done < fileB
另一种方法:您可以轻松生成四个文件名组
cat FileA | xargs -n4 echo
但是我想不出任何特别优雅的方法将它与FileB的输出文件名结合起来。 一种可能性是做一些字符串操作然后评估它(就像Didier Trosset的答案)。
编辑:得到它! 使用GNU parallel(就像类固醇上的xargs):
parallel < tempA -n4 -k --files cat | paste - tempB | xargs -n 2 mv
parallel
命令在每组4个参数上运行cat,并将输出放入临时文件中。 它将这些临时文件的名称写入stdout( -k
表示它们以正确的顺序写出)。
paste
将所需的文件名插入到流中,然后我们只使用xargs -n 2 mv
将临时文件移动到所需的位置。
我使用< tempA
而不是cat tempA
因为它是技术上的最佳实践 。
相比其他一个衬里的优点(在我看来)是你没有eval字符串(例如使用bash
)。
使用awk
:
awk '{ORS=(NR%4?" ":"\n")}1' FileA | awk '{print "cat "$0" > "}' | paste - FileB | bash
或者,使用dshepherd
方法进行第一步:
xargs -n4 echo < FileA | awk '{print "cat "$0" >"}' | paste - FileB | bash
我发现这些非常干净,可扩展且合乎逻辑。
最简单的方法(虽然方法不太通用,而且IMO不那么“漂亮”),为每个xargs
分组添加"cat"
,并在paste
命令中附加>
作为分隔符:
xargs -n4 echo cat < FileA | paste -d ">" - FileB | bash
说明:
使用awk
,将每组四行组成一行。 如果记录号RN
是模4,则用新行"\\n"
分隔,否则单个空格" "
。 这给出了输出:
$ awk '{ORS=(NR%4?" ":"\\n")}1' FileA GSM1328513 GSM1328514 GSM1328515 GSM1328516 GSM1328545 GSM1328546 GSM1328547 GSM1328548 GSM1328609 GSM1328610 GSM1328611 GSM1328612
正如dshepherd
所建议的dshepherd
,使用xargs
更容易完成:
$ xargs -n4 < FileA GSM1328513 GSM1328514 GSM1328515 GSM1328516 GSM1328545 GSM1328546 GSM1328547 GSM1328548 GSM1328609 GSM1328610 GSM1328611 GSM1328612
现在,对于这些行中的每一行,前缀为cat
并附加>
。
$ xargs -n4 < FileA | awk '{print "cat "$0" > "}' cat GSM1328513 GSM1328514 GSM1328515 GSM1328516 > cat GSM1328545 GSM1328546 GSM1328547 GSM1328548 > cat GSM1328609 GSM1328610 GSM1328611 GSM1328612 >
加入这些线,从线FileB
,采用paste - FileB
(中-
这意味着从标准输入来获得。
$ xargs -n4 < FileA | awk '{print "cat "$0" > "}' | paste - FileB cat GSM1328513 GSM1328514 GSM1328515 GSM1328516 > Brn cat GSM1328545 GSM1328546 GSM1328547 GSM1328548 > Hrt cat GSM1328609 GSM1328610 GSM1328611 GSM1328612 > Lng
通过管道到bash
执行这些行中的每一行作为bash
命令:
xargs -n4 < FileA | awk '{print "cat "$0" > "}' | paste - FileB | bash
一个班轮:
cat FileA | sed 'N;N;N;s/\n/ /g;s/^/cat /;s/$/ >/;' | paste - FileB | bash
您可以通过删除最后一个bash
管道来测试实际生成的命令。
对于每一行FileA
,获得未来三年N
,转换换行\\n
为空格 ,prepend
cat
,并追加>
。 然后合并每个生成的行-
使用FileB
。 将这些命令发送给bash
。
甚至更短的sed
,在一个替换命令中添加cat
和>
。
cat FileA | sed 'N;N;N;s/\n/ /g;s/.*/cat & >/;' | paste - FileB | bash
使用bash
数组(需要bash
4或更高版本)。 我也假设名称的数量fileB
匹配与名称的数量fileA
。
readarray -t gsms < FileA
for ((i=0; i<${#gsms[@]}; i+=4)); do
read fname
echo "${gsms[@]:i:4}" > "$fname"
done < FileB
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.