For 循环从多个文件绘制

For 循环从多个文件绘制

我对 Unix 很陌生,想转换我以前在 R 中使用的脚本。为此,我有三个不同的文件(长度相等),它们具有不同的文件名变体。我想逐行迭代所有三个文件,从每个文件中取出一行。但是我不确定如何将多个变量合并到 for 循环中。

module load bbtools 

for i in fna and j in fna2 and k in fna.prefix; 
do 
rename.sh -Xmx20g in=",${i}," out=",${j}" prefix=",${k}"
done

有什么方法可以直接运行它而不调用 R 脚本吗?

答案1

您可以使用该paste命令。对于文件:

a.txt

1
2
3

b.txt

a
b
c

c.txt

z
x
y

命令输出示例如下:

$ paste -d, a.txt b.txt c.txt
1,x,a
2,y,b
3,z,c

然后您可以通过管道传输它(使用正确的分隔符,即您在命令中使用的分隔符paste):

paste -d, a.txt b.txt c.txt | while IFS=',' read -r f1 f2 f3; do
    # do your task with $f1 $f2 $3
done

答案2

您需要将 3 个文件加载到 3 个单独的数组中,然后迭代它们一次。检查数组的长度是否相同也是一个好主意。

bash有一个内置函数,用于mapfile将文本文件读入数组。虽然有一些选项可以更改 的默认行为mapfile,但默认情况下,输入文件的每一行都会加载到一个数组元素中。同样默认情况下,maparray使用 0 作为数组的原点,但您可以使用选项覆盖它-O(例如,-O 1从 1 而不是 0 开始数组)。

在 bash 中运行help mapfile,或搜索 bash 手册页以获取有关映射文件的详细信息。

例如:

#!/bin/bash

# load the three files into arrays a, b, and c.
mapfile -t -O 1 a < fna
mapfile -t -O 1 b < fna2
mapfile -t -O 1 c < fna.prefix

# check if they're the same length    
if [ "${#a[@]}" != "${#b[@]}" ] || [ "${#a[@]}" != "${#c[@]}" ]; then
  echo "input files are not the same length"
  exit 1
fi

# do something with them, iterating from 1 to the length of array a.    
for i in $(seq 1 "${#a[@]}"); do
  rename.sh -Xmx20g in=",${a[i]}," out=",${b[i]}" prefix=",${c[i]}"
done

注意:如果这 3 个文件很大,每个文件有数百万或数十亿行,这将使用不可行的内存量。最好使用一种语言,可以轻松地一次打开 3 个文件,并在循环的每次迭代中从每个文件中读取一行。例如 awk、perl、python、C 等,甚至 R。

相关内容