每第 i 个字符打印子串
Print substrings every ith character
我有一些文件,我想以 "sliding window" 的方式分成子字符串,以 1 个字符为增量。每个文件只有一行,我可以像这样打印子字符串:
input="file.txt"
awk '{print substr(,1,21)}' $input
awk '{print substr(,2,21)}' $input
分别给我以下输出。
AATAAGGTGCCTGATTAAA-G
ATAAGGTGCCTGATTAAA-GG
输入文件包含大约 17k 个字符,我设法尝试执行一个 for 循环来计算字符数,并在 for 循环中尝试上述命令,如下所示:
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr(,$num,21)}' $input
done
但是这个 returns 空输出。我还想 运行 它作为一个 bash 脚本,在命令行中指定输入和子字符串的大小以及输出文件,如:
script.sh input_file.txt 21 output.txt
我试过了,但还是不行。
input=
kmer=
output=
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr(,$num,$kmer)}' $input > $output
done
关于我做错了什么的任何提示?我是 awk 的新手...
#!/usr/bin/env bash
input=
kmer=
output=
data=$(<"$input")
for ((i=0;i<${#data};i++)); do
echo "${data:i:kmer}"
done > "$output"
它仅使用 substring expansion,引用手册:
${parameter:offset:length}
This is referred to as Substring Expansion.
It expands to up to length
characters of the value of parameter
starting at the character specified by offset
.
使用gawk
:
awk -v num="$kmer" '{for(i=1;i<=length([=11=]);i++) print substr([=11=],i,num)}' "$input" > "$output"
这是一个快得多的解决方案。速度差异显着:测试 17k 个字符和 30 个字符 window:第一个解决方案 ~10s,~0.01s 为第二个解决方案。
您也可以使用 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
是 "sliding window" 尺寸 + 1。
要处理文件中的数据而不是 STDIN,只需在 sed 命令后指定它:
sed -r ':loop h;s/.//3g;p;x; s/.//; t loop' myfile
$ echo {1..9} | tr -d ' ' | # create test data
awk -v len=3 '{n=length([=10=]); for(i=1;i<=n-len+1;i++) print substr([=10=],i,len)}'
123
234
345
456
567
678
789
关于您的特定问题,代码段:
awk '{print substr(,$num,21)}' $input
存在一个问题,即单引号内的内容 而不是 受 shell 变量扩展的影响。这可以通过以下方式看到:
pax$ num=42 && echo '$num'
$num
pax$ num=42 && echo "$num"
42
因此 $num
将 而不是 替换为 shell 变量的值。
从上面也可以看出,你可以使用双引号,将允许扩展,但是你需要转义</code>到<em>防止</em> 它的扩展。我通常发现将 shell 变量转换为 <code>awk
变量更容易,如下所示:
awk -vnum=$num '{print substr(,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
在我的系统上,这会在大约 10 秒内完成。 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([=16=]); i++) {print substr([=16=], i, 21)}}' inputdata.txt
给出了更令人印象深刻的(大约十分之一秒):
real 0m0.121s
user 0m0.008s
sys 0m0.016s
需要 perl 中的条目吗?
#! /bin/env perl
use strict;
use warnings;
my $data;
my $offset = 0;
my $window = shift or die "Use: [=10=] {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
我有一些文件,我想以 "sliding window" 的方式分成子字符串,以 1 个字符为增量。每个文件只有一行,我可以像这样打印子字符串:
input="file.txt"
awk '{print substr(,1,21)}' $input
awk '{print substr(,2,21)}' $input
分别给我以下输出。
AATAAGGTGCCTGATTAAA-G
ATAAGGTGCCTGATTAAA-GG
输入文件包含大约 17k 个字符,我设法尝试执行一个 for 循环来计算字符数,并在 for 循环中尝试上述命令,如下所示:
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr(,$num,21)}' $input
done
但是这个 returns 空输出。我还想 运行 它作为一个 bash 脚本,在命令行中指定输入和子字符串的大小以及输出文件,如:
script.sh input_file.txt 21 output.txt
我试过了,但还是不行。
input=
kmer=
output=
count=`wc -c ${input} |cut -d' ' -f1`
for num in `seq ${count}`
do
awk '{print substr(,$num,$kmer)}' $input > $output
done
关于我做错了什么的任何提示?我是 awk 的新手...
#!/usr/bin/env bash
input=
kmer=
output=
data=$(<"$input")
for ((i=0;i<${#data};i++)); do
echo "${data:i:kmer}"
done > "$output"
它仅使用 substring expansion,引用手册:
${parameter:offset:length}
This is referred to as Substring Expansion. It expands to up to
length
characters of the value ofparameter
starting at the character specified byoffset
.
使用gawk
:
awk -v num="$kmer" '{for(i=1;i<=length([=11=]);i++) print substr([=11=],i,num)}' "$input" > "$output"
这是一个快得多的解决方案。速度差异显着:测试 17k 个字符和 30 个字符 window:第一个解决方案 ~10s,~0.01s 为第二个解决方案。
您也可以使用 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
是 "sliding window" 尺寸 + 1。
要处理文件中的数据而不是 STDIN,只需在 sed 命令后指定它:
sed -r ':loop h;s/.//3g;p;x; s/.//; t loop' myfile
$ echo {1..9} | tr -d ' ' | # create test data
awk -v len=3 '{n=length([=10=]); for(i=1;i<=n-len+1;i++) print substr([=10=],i,len)}'
123
234
345
456
567
678
789
关于您的特定问题,代码段:
awk '{print substr(,$num,21)}' $input
存在一个问题,即单引号内的内容 而不是 受 shell 变量扩展的影响。这可以通过以下方式看到:
pax$ num=42 && echo '$num'
$num
pax$ num=42 && echo "$num"
42
因此 $num
将 而不是 替换为 shell 变量的值。
从上面也可以看出,你可以使用双引号,将允许扩展,但是你需要转义</code>到<em>防止</em> 它的扩展。我通常发现将 shell 变量转换为 <code>awk
变量更容易,如下所示:
awk -vnum=$num '{print substr(,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
在我的系统上,这会在大约 10 秒内完成。 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([=16=]); i++) {print substr([=16=], i, 21)}}' inputdata.txt
给出了更令人印象深刻的(大约十分之一秒):
real 0m0.121s
user 0m0.008s
sys 0m0.016s
需要 perl 中的条目吗?
#! /bin/env perl
use strict;
use warnings;
my $data;
my $offset = 0;
my $window = shift or die "Use: [=10=] {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