有没有办法对bash中括号内的数字进行操作?

时间:2017-07-18 00:05:15

标签: bash perl unix awk

我的输入文件包含一些格式的行 -

  

这里的任意文字{{x1 x2} {x3 x4}}这里的任意文字

其中,x1,x2,x3和x4是浮点数和/或整数。有没有办法,比如说,使用awk / perl / bash脚本将这些数字加倍(总是用花括号括起来)?

我尝试使用以下变体,但我是awk的新手,并且甚至无法在花括号中隔离数字 -

  

awk -F('{gsub(“{}”,“”,$ NF);打印$ NF}'

示例 -

输入:

  

此处的任意文字1 {{1 2} {3 4}}任意文字1

     

此处的任意文字2 {{2.0 4} {6.0 8}}任意文字2

     

此处任意text2p5

     

任意文字3 {{3 6} {9 12}}任意文字3

     

任意文字4 {{4 8} {12 16}}任意文字4

输出:

  

此处的任意文字1 {{2 4} {6 8}}任意文字1

     

此处的任意文字2 {{4.0 8} {12.0 16}}此处任意文字2

     

此处任意text2p5

     

任意文字3 {{6 12} {18 24}}任意文字3

     

任意文字4 {{8 16} {24 32}}任意文字4

2 个答案:

答案 0 :(得分:1)

您可以捕获所需的模式并使用替换来重写字符串。

这是一个基本方法:首先捕获组件,处理它们,然后重新组装

use warnings;
use strict;

my $str = 'Arbitrary text here {{1 2} {3 4}} arbitrary text here';

my @parts = $str =~ /(.*?){{(\d+) (\d+)} {(\d+) (\d+)}}(.*)/;

# If we expect only lines in the above format test and handle the error
if (@parts != 6) {
    die "Didn't find expected patterns in: $str";
}

my $pre_text  = shift @parts;
my $post_text = pop @parts;

my ($r1, $r2, $r3, $r4) = map { $_*2 } @parts;

my $result = $pre_text . "{{$r1 $r2}{$r3 $r4}}" . $post_text;

print $result, "\n";

代码假定输入的确切格式如图所示。这个"手册的一个优点是"逐步的方法是,在需要时更容易调整过程的每个部分。

这可以在一个正则表达式中完成。由于盲目地信任预期的数据格式主要是一个非常糟糕的主意,我们可以将替换代码放在sub中, 这样就可以检查匹配并根据需要更容易地调整处理

sub repl {
    my @nums = @_;
    die "Expected four numbers, got: @nums" if @nums != 4;

    my ($r1, $r2, $r3, $r4) = map { $_ * 2 } @nums;    

    return "{{$r1 $r2} {$r3 $r4}}";
}

$str =~ s/{{(\d+) (\d+)} {(\d+) (\d+)}}/repl($1, $2, $3, $4)/e;

这也大大清理了正则表达式本身。

如果模式不匹配则没有任何反应,$str保持不变。如果我们只期望这种格式的行,那么我们可能想知道失败的匹配。了解它的一种方法是

if (not $str =~ s/.../) { warn "Failed match on: $str" }

因为替换算子s/返回了替换数。

更新以提供输入示例

上面的单正则表达式方法,文件input.txt包含输入行

use warnings;
use strict;

my $file = 'input.txt';
open my $fh, '<', $file  or die "Can't open $file: $!";

while (<$fh>) {
    s/{{(\d+) (\d+)} {(\d+) (\d+)}}/repl($1, $2, $3, $4)/e;
    print;
}

sub repl {
    my @nums = @_; 
    die "Expected four numbers, got: @nums" if @nums != 4;
    my ($r1, $r2, $r3, $r4) = map { $_ * 2 } @nums;    
    return "{{$r1 $r2} {$r3 $r4}}";
}

打印

Arbitrary text1 here {{2 4} {6 8}} arbitrary text1 here
Arbitrary text2 here {{4 8} {12 16}} arbitrary text2 here
Arbitrary text2p5 here
Arbitrary text3 here {{6 12} {18 24}} arbitrary text3 here
Arbitrary text4 here {{8 16} {24 32}} arbitrary text4 here

子中的行die ...仅用于保护它,可能用于其他用途。在我们使用它的正则表达式中,除非找到所有四个匹配项,否则从不调用sub。

上面的第一种方法,将代码置于输入行的循环中,打印出相同的内容。

答案 1 :(得分:0)

以下是Ruby中的一个示例。

$ echo "Arbitrary text here {{1.22 -3.55} {6.77 1e66}} arbitrary text here" | 
  ruby -lane 'p $_[/^[^{]*/] << 
                    $_[/{.*}/].gsub!(/[^ {}]+/) {|f| f.to_f*2} <<
                    $_[/[^}]*$/]'
"Arbitrary text here {{2.44 -7.1} {13.54 2.0e+66}} arbitrary text here"

Perl:

$ echo "Arbitrary text here {{1.22 -3.55} {6.77 1e66}} arbitrary text here" | 
perl -lane '$lh = $1 if m/(^[^{]*)/; 
            $rh = $1 if m/([^}]*)$/;
            $tgt = $1 if m/(\{.*\})/;
            $tgt =~ s/([^{} ]+)/$1*2/ge;  # the "e" executes the sub as Perl code
            print "$lh$tgt$rh\n";'
Arbitrary text here {{2.44 -7.1} {13.54 2e+66}} arbitrary text here

这里的两种方法都是将开头{和关闭}视为'寻找浮动并用它做某事'行为的开始。例如,没有平衡括号的解析。如果在这种情况下是独立的,则Perl和Ruby都会将非数字视为0.0,如果在数字字符串中,则将数字终止:

$ echo "Arbitrary text here {{1.22 -3.55 aa} {6s.77 1ye66}} arbitrary text here" | 
perl -lane '$lh = $1 if m/(^[^{]*)/; 
            $rh = $1 if m/([^}]*)$/;
            $tgt = $1 if m/(\{.*\})/;
            $tgt =~ s/([^{} ]+)/$1*2/ge;  # the "e" executes the sub as Perl code
            print "$lh$tgt$rh\n";'
Arbitrary text here {{2.44 -7.1 0} {12 2}} arbitrary text here

这可能是也可能不是一个特征......

随你更新。同样适用于整数:

$ echo "Arbitrary text1 here {{1 2} {3 4}} arbitrary text1 here
Arbitrary text2 here {{2 4} {6 8}} arbitrary text2 here
Arbitrary text2p5 here
Arbitrary text3 here {{3 6} {9 12}} arbitrary text3 here
Arbitrary text4 here {{4 8} {12 16}} arbitrary text4 here" | 
perl -lane '$rh=$lh=$tgt="";
        $lh = $1 if m/(^[^{]*)/; 
        $rh = $1 if m/([^}]*)$/;
        $tgt = $1 if m/(\{.*\})/;
        $tgt =~ s/([^{} ]+)/$1*2/ge;  # the "e" executes the sub as Perl code
        if ($rh && $lh && $tgt) {
            print "$lh$tgt$rh" ; 
        } else {
            print $_;    
        }'
Arbitrary text1 here {{2 4} {6 8}} arbitrary text1 here
Arbitrary text2 here {{4 8} {12 16}} arbitrary text2 here
Arbitrary text2p5 here
Arbitrary text3 here {{6 12} {18 24}} arbitrary text3 here
Arbitrary text4 here {{8 16} {24 32}} arbitrary text4 here