我有一些文件想以“滑动窗口”的方式分为子字符串,以1个字符为增量。每个文件只有一行,我可以这样打印子字符串:
input="file.txt"
awk '{print substr($1,1,21)}' $input
awk '{print substr($1,2,21)}' $input
分别给我以下输出。
AATAAGGTGCCTGATTAAA-G
ATAAGGTGCCTGATTAAA-GG
输入文件包含大约17,000个字符,我设法尝试执行for循环以计算字符并在for循环内尝试上述命令,如下所示:
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr($1,$num,21)}' $input
done
但是这将返回空输出。我还想将其作为bash脚本运行,并在命令行中指定输入和子字符串以及输出文件的大小,例如:
script.sh input_file.txt 21 output.txt
我尝试了一下,但是也没有用。
input=$1
kmer=$2
output=$3
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr($1,$num,$kmer)}' $input > $output
done
关于我在做什么的任何提示?我对awk很陌生...
答案 0 :(得分:3)
#!/usr/bin/env bash
input=$1
kmer=$2
output=$3
data=$(<"$input")
for ((i=0;i<${#data};i++)); do
echo "${data:i:kmer}"
done > "$output"
它仅使用substring expansion,引自手册:
${parameter:offset:length}
这称为子字符串扩展。 最多可扩展为
length
个值的parameter
个字符 从offset
指定的字符开始。
使用gawk
:
awk -v num="$kmer" '{for(i=1;i<=length($0);i++) print substr($0,i,num)}' "$input" > "$output"
这是一个快得多的解决方案。速度差异非常明显:在17,000个字符和30个字符的窗口上测试:第一个解决方案〜10s ,第二个解决方案〜0.01s 。
答案 1 :(得分:1)
您也可以使用GNU sed来执行此操作,如下所示:
echo -n "123456789" | sed -r ':loop h;s/.//3g;p;x; s/.//; t loop'
12
23
34
45
56
67
78
89
9
3g
是“滑动窗口”的大小+ 1。
要处理文件中的数据而不是STDIN,只需在sed命令后指定它即可:
sed -r ':loop h;s/.//3g;p;x; s/.//; t loop' myfile
答案 2 :(得分:1)
关于您的特定问题,摘要:
awk '{print substr($1,$num,21)}' $input
存在一个问题,即单引号内的内容不是受外壳变量扩展的影响。这可以通过以下方式看到:
pax$ num=42 && echo '$num'
$num
pax$ num=42 && echo "$num"
42
因此$num
将不会替换为shell变量的值。
从上面也可以看到,您可以使用双引号将 允许扩展,但是您需要转义$1
来防止其扩展。我通常会发现,将shell变量转换为awk
变量比较容易,如下:
awk -vnum=$num '{print substr($1,num,21)}' $input
以下代码片段显示了此操作:
pax$ num=42 && awk 'END{print $num}' </dev/null
pax$ num=42 && awk -v num=$num 'END{print num}' </dev/null
42
但是,外部程序的17,000次调用效率非常低,最好编译一些东西,或者,如果必须使用脚本编制,则可以完全在bash
本身中完成。下面的代码显示了如何执行此操作,其中的重要位在time ( )
块中,其他所有内容仅是设置测试数据,计时和清理。
# Create test data.
(
for i in {1..1000} ; do
echo -n "abcdefghijklmnop-"
done
) >inputdata.txt
# Time the execution.
time (
char17k="$(cat inputdata.txt)"
echo ${#char17k}
for ((i = 0; i < ${#char17k}; i++)) ; do
echo ${char17k:i:21}
done
)
# Clean up.
rm -rf inputdata.txt
在我的系统上,此过程大约需要十秒钟。即使没有做任何有用的工作,调用17,000 awk
所花费的时间也大约是此时间的三倍:
pax$ time (for in in {1..17000} ; do awk '{}' </dev/null ; done )
real 0m30.649s
user 0m5.196s
sys 0m4.848s
通过让awk
完成所有工作,您当然可以甚至更快地获得更多的速度。用以下代码替换上面代码中的time ( )
块的内容:
awk '{for (i = 1; i < length($0); i++) {print substr($0, i, 21)}}' inputdata.txt
给人留下深刻的印象(大约十分之一秒):
real 0m0.121s
user 0m0.008s
sys 0m0.016s
答案 3 :(得分:0)
$ echo {1..9} | tr -d ' ' | # create test data
awk -v len=3 '{n=length($0); for(i=1;i<=n-len+1;i++) print substr($0,i,len)}'
123
234
345
456
567
678
789
答案 4 :(得分:0)
需要输入perl吗?
#! /bin/env perl
use strict;
use warnings;
my $data;
my $offset = 0;
my $window = shift or die "Use: $0 {windowSize} [ < ] infile [ > outfile ]\n";
{ local $/;
$data = <>;
}
print "$_\n" while $_ = substr $data, $offset++, $window;
exit;
可以挤成一排,甚至可以使用严格的警告&c ...
$: wc -c src
17000 src
$: time ./slide 21 src
!"#$%&'()*+,-./012345
"#$%&'()*+,-./0123456
#$%&'()*+,-./01234567
$%&'()*+,-./012345678
。 。 。
WXYZ[\
XYZ[\
YZ[\
Z[\
[\
\
real 0m0.029s
user 0m0.004s
sys 0m0.021s