Bash复制每行,再打印3行,更改结尾
输入文件由多行组成,如Bash复制每行,再打印3行,更改结尾,bash,shell,awk,sed,Bash,Shell,Awk,Sed,输入文件由多行组成,如 0 1 0 0 0 1 1 0 0 0 / 1 0 / 1 0 / 1 0 1 0 1 0 0 0 0 -1 3 / 4 1 / 4 1 / 2 我想复制输入中的每一行,在原始行下面插入3个副本,并修改末尾的分数。我希望输出是 0 1 0 0 0 1
0 1 0 0 0 1 1 0 0 0 / 1 0 / 1 0 / 1
0 1 0 1 0 0 0 0 -1 3 / 4 1 / 4 1 / 2
我想复制输入中的每一行,在原始行下面插入3个副本,并修改末尾的分数。我希望输出是
0 1 0 0 0 1 1 0 0 0 / 1 0 / 1 0 / 1
0 1 0 0 0 1 1 0 0 0 / 1 1 / 2 1 / 2
0 1 0 0 0 1 1 0 0 1 / 2 0 / 1 1 / 2
0 1 0 0 0 1 1 0 0 1 / 2 1 / 2 0 / 1
0 1 0 1 0 0 0 0 -1 3 / 4 1 / 4 1 / 2
0 1 0 1 0 0 0 0 -1 3 / 4 3 / 4 0 / 1
0 1 0 1 0 0 0 0 -1 1 / 4 1 / 4 0 / 1
0 1 0 1 0 0 0 0 -1 1 / 4 3 / 4 1 / 2
对分数的修改遵循该模式
(0,0,0) <- original fractions
(0,+1/2,+1/2)
(+1/2,0,+1/2)
(+1/2,+1/2,0)
必须从中减去1
so 5/4 -> 1/4
希望将此解决方案添加到当前的bash脚本中。我显示为“输入”的内容是迄今为止脚本的结果。也许是一个awk或sed命令来实现所需的结果?下面的Perl脚本按照您要求的方式处理分数。它用来做数学运算
#!/usr/bin/perl
use strict;
use warnings;
use Number::Fraction;
while (<>) {
print;
my @cols = split /(\s+)/;
for my $modify ([0, 1, 1], [1, 0, 1], [1, 1, 0]) {
my @fractions = map 'Number::Fraction'->new(@cols[@$_]),
[-18, -14],[-12, -8], [-6, -2];
my @newcols;
for my $i (0 .. 2) {
if ($modify->[$i]) {
$fractions[$i] += 'Number::Fraction'->new(1, 2);
$fractions[$i] -= 1 if $fractions[$i] >= 1;
}
push @newcols, $fractions[$i];
}
s{/}{ / }, s{^0$}{0 / 1} for @newcols; # Format the fractions.
print @cols[0..19], $newcols[0],
$cols[25], $newcols[1],
$cols[31], $newcols[2],
"\n";
}
}
#/usr/bin/perl
严格使用;
使用警告;
使用数字::分数;
而(){
打印
my@cols=split/(\s+);
对于我的$modify([0,1,1],[1,0,1],[1,1,0]){
my@fractions=map'Number::Fraction'->新建(@cols[@$)),
[-18, -14],[-12, -8], [-6, -2];
我的@newcols;
对于我的$i(0..2){
如果($modify->[$i]){
$fractions[$i]+='Number::Fraction'->新的(1,2);
如果$fracts[$i]>=1,则$fracts[$i]-=1;
}
推送@newcols,$sections[$i];
}
@newcols;#的{/}{/},s{^0$}{0/1}格式化分数。
打印@cols[0..19],$newcols[0],
$cols[25],$newcols[1],
$cols[31],$newcols[2],
“\n”;
}
}
awk
救援
$ awk 'BEGIN{FS=OFS="\t"}
function addHalf(v) {split(v,a," / "); # split num/denom
n=2*a[1]+a[2]; d=2*a[2]; # add 1/2
if(n>=d) n-=d; # modulus 1
while(!(n%2 || d%2)) {n/=2;d/=2} # normalize if both even
return n " / " d}
{print;
for(i=2;i>=0;i--) # iterate over last three fields
{j=NF-(i+1)%3; k=NF-(i+2)%3; # compute indices
tj=$j; tk=$k; # save values
$j=addHalf(tj); $k=addHalf(tk); # modify selected indices
print; # print modified line
$j=tj; $k=tk}}' file # revert to saved values
这将帮助您使用GNU awk for true multi-dimensional Array、gensub()和\s/\s速记识别原始分数和要应用于每个分数的增量:
$ cat tst.awk
BEGIN {
split("\
(0,0,0) \
(0,+1/2,+1/2) \
(+1/2,0,+1/2) \
(+1/2,+1/2,0) \
",modRows,/[[:space:])(]+/)
for (i=1; i in modRows; i++) {
row = modRows[i]
if ( row ~ /\S/ ) {
deltas[++numRows][1]
numCols = split(row,deltas[numRows],/,/)
}
}
}
{
head = gensub(/^((\s*\S+){9})(.*)/,"\\1",1)
tail = gensub(/^(\s*(\S+\s+){9})(.*)/,"\\3",1)
tail = gensub(/ ([^0-9]) /,"\\1","g",tail)
split(tail,fracts)
for (rowNr=1; rowNr <= numRows; rowNr++) {
printf "%s", head
for (colNr=1; colNr <= numCols; colNr++) {
fract = fracts[colNr]
delta = deltas[rowNr][colNr]
printf "%s%s", OFS, addDelta(fract,delta)
}
print ""
}
}
function addDelta(oldFract,delta, newFract) {
newFract = "(" oldFract " + " delta ")" # <-- do the math here!
return newFract
}
<>你需要做的就是把你计算出来的任何数学加在脚本底部AdDeltSase()函数中的每个点上的计算。当你在做中等复杂的算术运算(添加分数和模1)时,我会考虑为此编写小Perl或Python脚本。虽然它在bash和awk中是可以自己实现的(sed我不确定——可能吧?),但可读性会受到很大的影响。我应该提到我对脚本编写是新手,因此我对perl和pythonSo不熟悉。您是否会要求我们每一步都做您的工作(或家庭作业)?在前面的问题之后,您要求生成此问题的输入。工作吧,如果你被困在某件事上,我们会帮助你,但不是全部写下来!过去的帖子:那么……仅供参考,既然你在个人资料中说“我想加入一个计算研究小组”,你可能想学习python。Python有很多数学、统计、图形工具。。。模块。到目前为止,Bash对数学不太好!Bash更适合系统管理员,而不是复杂的逻辑。我发布了一个解决方案,但应该说这是一种可怕的工作格式,它既不是机器也不是人类友好的!哦,我的天哪,当你说它既不是机器也不是人类友好型的时候,你不是在开玩笑。但是考虑到OP对其编程经验的评价,将其保存在AWK中可能是一个很好的选择,这样OP才能更好地理解它。第10行:(FILENAME=input.txt FNR=1)致命:尝试访问字段-1我们是否应该迭代最后9个字段并跳过/symbol?字段选项卡是否分开?如果没有,你就不会有最后三个字段,而只有一个。要分开每一个“列”,我只需按适当的空格次数,这是不对的。那么,您如何以原子方式定义最后三个字段?它们也是空间分隔的。您可以使用
unexpand-t5 file>file.tsv
change 5将格式更改为任意数量的空格。或者,类似于sed
。主要问题是您不应在字段中使用无效字符的字段分隔符。尝试使用您的代码将产生以下结果:第41行:use:command未找到第42行:use:command未找到第44行:use:command未找到第46行:意外标记附近的语法错误
)'第46行:while(){'
我已经检查过了,我有PerlV5.10。1@RobS.您是如何运行Perl脚本的?要么chmod u+x
然后运行/path/to/script input/file
,要么使用Perl path/to/script input/file
运行它。您显示的错误来自shell,而不是Perl。现在我收到了-bash:./Perl\u输出:bin/Perl:bad解释器:没有这样的文件或目录
,无法在@INC中找到Number/Fraction.pm(@INC包含:/usr/local/lib64/perl5/usr/local/share/perl5/usr/lib64/perl5/vendor\u perl/usr/share/perl5/vendor\u perl perl/usr/lib64/perl5/usr/share/perl5。)在perl_输出行5.BEGIN失败——编译在perl_输出行5中止。
使用您列出的两种方法,您需要通过运行cpan Number::Fraction
来安装Number::Fraction。但是,首先,您需要配置cpan
以正确安装库,可能是通过。我收到错误BEGIN:not found synta命令意外标记附近的x错误“(0,0,0)(0,+1/2,+1/2)(+1/2,0,+1/2)(+1/2,+1/2,0)”,modRows,/[:space:'try03.bash:第47行:
”,modRows,/[:space:])(]+/)“然后你把我的脚本复制/粘贴错了,或者犯了其他错误。我无法帮助你找出错误是什么,因为我看不到你的代码。”。
$ cat tst.awk
BEGIN {
split("\
(0,0,0) \
(0,+1/2,+1/2) \
(+1/2,0,+1/2) \
(+1/2,+1/2,0) \
",modRows,/[[:space:])(]+/)
for (i=1; i in modRows; i++) {
row = modRows[i]
if ( row ~ /\S/ ) {
deltas[++numRows][1]
numCols = split(row,deltas[numRows],/,/)
}
}
}
{
head = gensub(/^((\s*\S+){9})(.*)/,"\\1",1)
tail = gensub(/^(\s*(\S+\s+){9})(.*)/,"\\3",1)
tail = gensub(/ ([^0-9]) /,"\\1","g",tail)
split(tail,fracts)
for (rowNr=1; rowNr <= numRows; rowNr++) {
printf "%s", head
for (colNr=1; colNr <= numCols; colNr++) {
fract = fracts[colNr]
delta = deltas[rowNr][colNr]
printf "%s%s", OFS, addDelta(fract,delta)
}
print ""
}
}
function addDelta(oldFract,delta, newFract) {
newFract = "(" oldFract " + " delta ")" # <-- do the math here!
return newFract
}
$ gawk -f tst.awk file
0 1 0 0 0 1 1 0 0 (0/1 + 0) (0/1 + 0) (0/1 + 0)
0 1 0 0 0 1 1 0 0 (0/1 + 0) (0/1 + +1/2) (0/1 + +1/2)
0 1 0 0 0 1 1 0 0 (0/1 + +1/2) (0/1 + 0) (0/1 + +1/2)
0 1 0 0 0 1 1 0 0 (0/1 + +1/2) (0/1 + +1/2) (0/1 + 0)
0 1 0 1 0 0 0 0 -1 (3/4 + 0) (1/4 + 0) (1/2 + 0)
0 1 0 1 0 0 0 0 -1 (3/4 + 0) (1/4 + +1/2) (1/2 + +1/2)
0 1 0 1 0 0 0 0 -1 (3/4 + +1/2) (1/4 + 0) (1/2 + +1/2)
0 1 0 1 0 0 0 0 -1 (3/4 + +1/2) (1/4 + +1/2) (1/2 + 0)