追加两列,在 Unix 中的每个值处添加一个特定的整数
Append two columns adding a specific integer at each value in Unix
我有两个这样的文件:
# step distance
0 4.48595407961296e+01
2500 4.50383737781376e+01
5000 4.53506757198727e+01
7500 4.51682465277482e+01
10000 4.53410353656445e+01
# step distance
0 4.58854106214881e+01
2500 4.58639266431320e+01
5000 4.60620560167519e+01
7500 4.58990075106227e+01
10000 4.59371359946124e+01
所以我想将两个文件连接在一起,同时保持间距。
特别是,第二个文件需要记住第一个文件的结束值并从第一个文件开始计数。
输出:
# step distance
0 4.48595407961296e+01
2500 4.50383737781376e+01
5000 4.53506757198727e+01
7500 4.51682465277482e+01
10000 4.53410353656445e+01
12500 4.58854106214881e+01
15000 4.58639266431320e+01
17500 4.60620560167519e+01
20000 4.58990075106227e+01
22500 4.59371359946124e+01
使用 calc 很容易解决的问题是间距需要正确才能工作,在这种情况下 calc 会变得一团糟。
Perl 来拯救!
#!/usr/bin/perl
use warnings;
use strict;
open my $F1, '<', 'file1' or die $!;
my ($before, $after, $diff);
my $max = 0;
while (<$F1>) {
print;
my ($space1, $num, $space2) = /^(\s*) ([0-9]+) (\s*)/x or next;
($before, $after) = ($space1, $space2);
$diff = $num - $max;
$max = $num;
}
$before = length "$before$max"; # We'll need it to format the computed numbers.
open my $F2, '<', 'file2' or die $!;
<$F2>; # Skip the header.
while (<$F2>) {
my ($step, $distance) = split;
$step += $max + $diff;
printf "% ${before}d%s%s\n", $step, $after, $distance;
}
程序会记住 $max 中的最后一个数字。它还保留前导 whitespace 加上 $before 中的 $max 的长度,以格式化所有未来数字以占用相同的 space(使用 printf)。
您没有说明距离列是如何对齐的,即
20000 4.58990075106227e+01
22500 11.59371359946124e+01 # dot aligned?
22500 11.34572478912301e+01 # left aligned?
程序会以后一种方式对齐它。如果您想要前者,请使用与步骤列类似的技巧。
# start awk and set the *Step* between file to 2500
awk -v 'Step=2500' '
# 1st line of 1 file (NR count every line, from each file) init and print header
NR == 1 {LastFile = FILENAME; OFS = "\t"; print}
# when file change (new filename compare to previous line read)
# Set a new index (for incremental absolute step from relative one) and new filename reference
FILENAME != LastFile { StartIndex = LastIndex + Step; LastFile = FILENAME}
# after first line and for every line stating witha digit (+ space if any)
# calculate absolute step and replace relative one, print the new content
NR > 1 && /^[[:blank:]]*[0-9]/ { += StartIndex; LastIndex = ;print }
' YourFiles*
- 结果取决于文件顺序
- 输出分隔符由 OFS 值设置(此处为制表符)
我有两个这样的文件:
# step distance
0 4.48595407961296e+01
2500 4.50383737781376e+01
5000 4.53506757198727e+01
7500 4.51682465277482e+01
10000 4.53410353656445e+01
# step distance
0 4.58854106214881e+01
2500 4.58639266431320e+01
5000 4.60620560167519e+01
7500 4.58990075106227e+01
10000 4.59371359946124e+01
所以我想将两个文件连接在一起,同时保持间距。 特别是,第二个文件需要记住第一个文件的结束值并从第一个文件开始计数。
输出:
# step distance
0 4.48595407961296e+01
2500 4.50383737781376e+01
5000 4.53506757198727e+01
7500 4.51682465277482e+01
10000 4.53410353656445e+01
12500 4.58854106214881e+01
15000 4.58639266431320e+01
17500 4.60620560167519e+01
20000 4.58990075106227e+01
22500 4.59371359946124e+01
使用 calc 很容易解决的问题是间距需要正确才能工作,在这种情况下 calc 会变得一团糟。
Perl 来拯救!
#!/usr/bin/perl
use warnings;
use strict;
open my $F1, '<', 'file1' or die $!;
my ($before, $after, $diff);
my $max = 0;
while (<$F1>) {
print;
my ($space1, $num, $space2) = /^(\s*) ([0-9]+) (\s*)/x or next;
($before, $after) = ($space1, $space2);
$diff = $num - $max;
$max = $num;
}
$before = length "$before$max"; # We'll need it to format the computed numbers.
open my $F2, '<', 'file2' or die $!;
<$F2>; # Skip the header.
while (<$F2>) {
my ($step, $distance) = split;
$step += $max + $diff;
printf "% ${before}d%s%s\n", $step, $after, $distance;
}
程序会记住 $max 中的最后一个数字。它还保留前导 whitespace 加上 $before 中的 $max 的长度,以格式化所有未来数字以占用相同的 space(使用 printf)。
您没有说明距离列是如何对齐的,即
20000 4.58990075106227e+01
22500 11.59371359946124e+01 # dot aligned?
22500 11.34572478912301e+01 # left aligned?
程序会以后一种方式对齐它。如果您想要前者,请使用与步骤列类似的技巧。
# start awk and set the *Step* between file to 2500
awk -v 'Step=2500' '
# 1st line of 1 file (NR count every line, from each file) init and print header
NR == 1 {LastFile = FILENAME; OFS = "\t"; print}
# when file change (new filename compare to previous line read)
# Set a new index (for incremental absolute step from relative one) and new filename reference
FILENAME != LastFile { StartIndex = LastIndex + Step; LastFile = FILENAME}
# after first line and for every line stating witha digit (+ space if any)
# calculate absolute step and replace relative one, print the new content
NR > 1 && /^[[:blank:]]*[0-9]/ { += StartIndex; LastIndex = ;print }
' YourFiles*
- 结果取决于文件顺序
- 输出分隔符由 OFS 值设置(此处为制表符)