每四个文件连接一次,Linux

每四个文件连接一次,Linux,linux,bash,concatenation,Linux,Bash,Concatenation,我有两个存储有文件名列表的文件: FileA: GSM1328513 GSM1328514 GSM1328515 GSM1328516 GSM1328545 GSM1328546 GSM1328547 GSM1328548 GSM1328609 GSM1328610 GSM1328611 GSM1328612 and: FileB: Brn Hrt Lng 我要做的是,将文件A中列出的每四个文件连接起来,并将连接的文件命名为文件B中列出的文件名: 要手动执行此操作,

我有两个存储有文件名列表的文件:

FileA:
GSM1328513
GSM1328514
GSM1328515
GSM1328516
GSM1328545
GSM1328546
GSM1328547
GSM1328548
GSM1328609
GSM1328610
GSM1328611
GSM1328612

and:
FileB:
    Brn
    Hrt
    Lng 
我要做的是,将文件A中列出的每四个文件连接起来,并将连接的文件命名为文件B中列出的文件名: 要手动执行此操作,它看起来像:

cat GSM1328513 GSM1328514 GSM1328515 GSM1328516 > Brn
cat GSM1328545 GSM1328546 GSM1328547 GSM1328548 > Hrt
cat GSM1328609 GSM1328610 GSM1328611 GSM1328612 > Lng
因为我有一长串的文件,我想自动完成,有人能帮忙吗。
如果有什么不清楚的地方,请指出。

这是执行您想要执行的操作的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
iter=0
读取文件名时
做
stop=`expr\($iter+1\)\*4`
iter=`expr$iter+1`
files=`head-n$stop fileA | tail-n4 | tr'\n''`
cat$files>$filename
完成
一个衬里:

cat FileA | sed 'N;N;N;s/\n/ /g;s/^/cat /;s/$/ >/;' | paste - FileB | bash
您可以通过删除最后一个管道到
bash
来测试实际生成的命令

对于
FileA
的每一行,获取下三行
N
,将换行
\N
转换为空格
,在
cat
前面加上前缀,然后追加
。然后将生成的每一行
-
文件B
中的一行合并。将这些命令发送到
bash


甚至更短的
sed
,在单个replace命令中添加了
cat

cat FileA | sed 'N;N;N;s/\n/ /g;s/.*/cat & >/;' | paste - FileB | bash

另一种不用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
,查看它在执行之前做了什么

其他方法:一条不带eval的班轮,将@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
粘贴
)。

另一种方法:您可以使用

cat FileA | xargs -n4 echo
然而,我想不出任何特别优雅的方式将其与FileB的输出文件名结合起来。一种可能是进行一些字符串操作,然后对其进行求值(如Didier Trosset的答案)

编辑:明白了!使用GNU并行(类似于类固醇上的xargs):

parallel
parallel
命令对每组4个参数运行cat,并将输出放入临时文件中。它将这些临时文件的名称写入标准输出(并且
-k
表示它们以正确的顺序写入)

paste
将所需的文件名插入流中,然后我们只需使用
xargs-n2 mv
将临时文件移动到所需的位置

我使用了
而不是
cat tempA
,因为从技术上讲它很简单


在我看来,与其他单行程序相比,这种方法的优点是不必求值字符串(例如,使用
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

说明:

  • 使用
    awk
    ,将每组四行组成一行。 如果记录编号,
    RN
    为模四,则用新行
    “\n”
    分隔,否则为单个空格
    。 这将产生以下输出:

    $ awk '{ORS=(NR%4?" ":"\n")}1' FileA
    GSM1328513 GSM1328514 GSM1328515 GSM1328516
    GSM1328545 GSM1328546 GSM1328547 GSM1328548
    GSM1328609 GSM1328610 GSM1328611 GSM1328612
    
    正如
    dshepherd
    所建议的,使用
    xargs
    更容易做到这一点:

    $ xargs -n4 < FileA
    GSM1328513 GSM1328514 GSM1328515 GSM1328516
    GSM1328545 GSM1328546 GSM1328547 GSM1328548
    GSM1328609 GSM1328610 GSM1328611 GSM1328612
    
  • 使用
    粘贴-FileB
    -
    意思是从标准输入中获取)将这些行与
    文件b
    中的行连接起来

    $ 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
    4或更高版本是必需的)
    fileB
    中的名称数与
    fileA
    中的名称数匹配

    readarray -t gsms < FileA
    for ((i=0; i<${#gsms[@]}; i+=4)); do
        read fname
        echo "${gsms[@]:i:4}" > "$fname"
    done < FileB
    
    readarray-t gsms
    您也可以在末尾使用
    粘贴
    cat FileA | xargs-n4 echo cat | paste-d'>'-FileB | bash
    。我认为这是最短的答案:)我真的很想找到一个不涉及字符串求值的单行程序,但我想不出怎么做。我认为我们需要某种方法将独立的参数列表传递给
    xargs
    ,但你不能……我同时想出了另一种方法!:)您的方法可能更简单。我需要开始使用
    ,而
    循环更多。
    
    $ xargs -n4 < FileA | awk '{print "cat "$0" > "}' 
    cat GSM1328513 GSM1328514 GSM1328515 GSM1328516 > 
    cat GSM1328545 GSM1328546 GSM1328547 GSM1328548 > 
    cat GSM1328609 GSM1328610 GSM1328611 GSM1328612 > 
    
    $ 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
    
    xargs -n4 < FileA  | awk '{print "cat "$0" > "}' | paste - FileB | bash
    
    readarray -t gsms < FileA
    for ((i=0; i<${#gsms[@]}; i+=4)); do
        read fname
        echo "${gsms[@]:i:4}" > "$fname"
    done < FileB