如何在 cut 命令中循环变量范围

How to loop a variable range in cut command

我有一个包含 2 列的文件,我想使用第二列中的值将剪切命令中的范围设置为 select 来自另一个文件的字符范围。我想要的范围是第二列中值所在位置的字符加上接下来的 10 个字符。一会儿我会举个例子。

我的文件是这样的:

包含 2 列且行与行之间没有空行的文件 (file1.txt):

NAME1 10
NAME2 25
NAME3 48
NAME4 66

我要提取可变字符范围的文件(只有很长的一行,没有空格,没有粗体)(file2.txt):

GATCGAGCGGGATTCTTTTTTTTTAGGCGAGTCAGCTAGCATCAGCTACGAGAGGCGAGGGCGGGCTATCACGACTACGACTACGACTACAGCATCAGCATCAGCGCACTAGAGCGAGGCTAGCTAGCTACGACTACGATCAGCATCGCACATCGACTACGATCAGCATCAGCTACGCATCGAAGAGAGAGC

...或者,更直白地说(用于 copy/paste 测试):

GATCGAGCGGGATTCTTTTTTTTTAGGCGAGTCAGCTAGCATCAGCTACGAGAGGCGAGGGCGGGCTATCACGACTACGACTACGACTACAGCATCAGCATCAGCGCACTAGAGCGAGGCTAGCTAGCTACGACTACGATCAGCATCGCACATCGACTACGATCAGCATCAGCTACGCATCGAAGAGAGAGC

想要的结果文件,每行一个序列(result.txt):

GATTCTTTTT
GGCGAGTCAG
CGAGAGGCGA
TATCACGACT

生成的文件将包含 10-20、25-35、48-58 和 66-76 中的字符,每个范围占一行。因此,它将始终保持 10 的范围,但在不同的起点,这些起点由第一个文件第二列中的值设置。

我试过命令:

for i in $(awk '{print }' file1.txt);
do
        p1=$i;
        p2=`expr "" + 10`
        cut -c$p1- file2.txt > result.txt;
done

我没有收到任何输出或错误消息。

我也试过:

while read line; do
    set $line
    p2=`expr "" + 10`
    cut -c-$p2 file2.txt > result.txt;
done <file1.txt

最后一条命令给我一条错误消息:

cut: invalid range with no endpoint: -
Try 'cut --help' for more information.
expr: non-integer argument

这里不需要cutdd 可以对文件进行索引,并且只读取你想要的字节数。 (请注意,status=none 是 GNUism;如果您想禁止信息日志记录,您可能需要在其他平台上将其保留并重定向 stderr)。

while read -r name index _; do
  dd if=file2.txt bs=1 skip="$index" count=10 status=none
  printf '\n'
done <file1.txt >result.txt

这种方法避免了过多的内存需求(如读取整个 file2 时出现的那样 - 假设它很大),并且具有有限的性能要求(开销等于启动 dd 的一个副本每个要提取的序列)。

一种解决方法:

#!/bin/bash                                                                                                        

while read line; do
    pos=$(echo "$line" | cut -f2 -d' ')
    x=$(head -c $(( $pos + 10 )) file2.txt | tail -c 10)
    echo "$x"
done < file1.txt > result.txt

这不是经验丰富的 bash 黑客会使用的解决方案,但对于 bash 的新手来说非常有用。它使用非常通用的工具,但如果您需要高性能,则有些糟糕。 Shell 脚本通常用于那些很少 shell 脚本但知道一些命令并且只想完成工作的人。这就是为什么我包括这个解决方案,即使其他答案对于更有经验的人来说更好。

第一行很简单。它只是从 file1.txt 中提取数字。第二行使用了非常好的工具 headtail。通常,它们与行而不是字符一起使用。不过,我用 head 打印前 pos + 10 个字符。结果通过管道传输到 tail 中,打印最后 10 个字符。

感谢@CharlesDuffy 的改进。

如果file2.txt不是太大,那么可以内存读取, 并使用 Bash 个子字符串来提取所需的范围:

data=$(<file2.txt)
while read -r name index _; do
  echo "${data:$index:10}"
done <file1.txt >result.txt

对于每个范围定义,这将比 运行 cut 或其他过程更有效。

(感谢 @CharlesDuffy 提供阅读 data 没有 无用 catwhile 循环的提示.)

使用 awk

$ awk 'FNR==NR{a=[=10=]; next} {print substr(a,+1,10)}' file2 file1
GATTCTTTTT
GGCGAGTCAG
CGAGAGGCGA
TATCACGACT