【问题标题】:Bash to copy every line, print 3 more, changing endBash复制每一行,再打印3个,改变结束
【发布时间】:2017-11-11 06:56:23
【问题描述】:

输入文件由多行组成,如

   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

i.e. 3/4 + 1/2 = 5/4

它必须减去 1

so 5/4 -> 1/4

想将此解决方案添加到我拥有的当前 bash 脚本中。我作为“输入”显示的是我的脚本迄今为止的结果。也许是一个 awk 或 sed 命令来达到预期的结果?

【问题讨论】:

  • 当您进行中等复杂的算术运算(添加分数和模 1)时,我会考虑为此编写小型 perl 或 python 脚本。虽然它在 bash 和 awk 中是可行的(我不确定 sed - 可能?),但可读性会受到很大影响。
  • 应该提到我是脚本新手,因此我不熟悉 perl 和 python
  • 那么您会要求我们在每一步都做您的工作(或家庭作业)吗?这遵循之前的问题,在这些问题中您要求对此问题进行输入。努力吧,如果您遇到困难,我们会提供帮助,但不要写完整的东西!过去的帖子:stackoverflow.com/questions/47192415/… 然后stackoverflow.com/questions/47215364/… 然后stackoverflow.com/questions/47214248/…...
  • 仅供参考,既然您在个人资料中说“我希望加入其中一个计算研究小组”,您可能想学习 python。 Python 有很多数学、统计、绘图工具……模块。到目前为止,Bash 对数学来说并不是很好! Bash 更适合系统管理员,而不是复杂的逻辑。
  • 我发布了一个解决方案,但应该说这是一种糟糕的格式,它既不是机器也不是人类友好的!

标签: bash shell awk sed


【解决方案1】:

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

【讨论】:

  • 哦,天哪,当您说它既不是机器也不是人类友好时,您不是在开玩笑。但考虑到 OP 所说的关于他的编程经验,将其保存在 AWK 中可能是一个不错的选择,以便 OP 能够最好地理解它。
  • 在尝试此操作时,我收到 awk: cmd. line:10: (FILENAME=input.txt FNR=1) fatal: attempt to access field -1 我们是否应该遍历最后 9 个字段并以某种方式跳过 / 符号?
  • 字段标签是否分开?如果没有,您将没有最后三个字段,而只有一个。
  • 为了分隔每个“列”,我只是按了适当的空间次数
  • 这是不对的。那么,您如何以原子方式定义最后三个字段?它们也是空间分隔的。您可以使用 unexpand -t5 file &gt; file.tsv 更改格式,将 5 更改为您拥有的任意数量的空格。或者,与sed 类似。主要问题是您不应该在字段中使用有效字符的字段分隔符。
【解决方案2】:

这将使您尽可能识别原始分数和要应用于每个分数的增量,使用 GNU awk 用于真正的多维数组、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
}

.

$ 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)

所以您需要做的就是在脚本底部的 addDeltas() 函数中的指定位置添加您计算出的任何数学来对每个分数进行计算。

【讨论】:

  • 我收到错误 BEGIN: command not found syntax error near unexpected token " (0,0,0) (0,+1/2,+1/2) (+1/2,0,+1/2) (+1/2,+1/2,0) ",modRows,/[[:space:]' try03.bash: line 47: ",modRows,/[[:space:])(]+/)'
  • 然后你复制/粘贴了我的脚本错误或犯了其他错误。由于我看不到您的代码,因此我无法帮助您找出错误所在。
【解决方案3】:

以下 Perl 脚本以您要求的方式处理分数。它使用Number::Fraction 进行数学运算。

#!/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";
    }

}

【讨论】:

  • 尝试您的代码会产生以下结果:line 41: use: command not found line 42: use: command not found line 44: use: command not found line 46: syntax error near unexpected token )' 第 46 行:while (&lt;&gt;) {' 我已经检查并且我有 perl v5.10.1
  • @RobS。你是如何运行 Perl 脚本的?要么chmod u+x 运行/path/to/script input/file,要么运行perl path/to/script input/file。您显示的错误来自 shell,而不是 Perl。
  • 现在我通过您列出的两种方法收到-bash: ./perl_output: bin/perl: bad interpreter: No such file or directoryCan't locate Number/Fraction.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at perl_output line 5. BEGIN failed--compilation aborted at perl_output line 5.
  • 您需要通过运行 cpan Number::Fraction 来安装 Number::Fraction。但首先,您需要配置cpan 才能正确安装库,可能是local::lib
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2016-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-16
  • 2016-01-14
相关资源
最近更新 更多