我对 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。