perl 如何格式化bash SED/AWK/Per输出以便进一步处理

1bqhqjot  于 2022-11-15  发布在  Perl
关注(0)|答案(6)|浏览(198)

我有一些文本文件数据,我正在解析与SED,AWK和Perl。

product {
    name { thing1 }
    customers {        
        mary { }
        freddy { }
        bob {
            spouse betty
        }
    }
}

从“customers”部分,我尝试获得类似以下内容的输出:

mary{ }
freddy{ }
bob{spouse betty}

使用:sed -n -e "/customers {/,/}/{/customers {/d;/}/d;p;}" $file'
输出如下:

mary { }
freddy { }
bob {
    spouse betty
}

我如何将“bob”customer连接到一行并删除多余的空格?产生此特定输出的主要原因是,我正在编写一个脚本,以获取文本文件中的“customer”字段和其他字段,然后将它们输出到csv文件中。它看起来像这样。我知道这在其他语言中可能更容易,但我知道bash。

output.csv
product,customers,another_column
thing1,mary{ } freddy{ } bob{spouse betty},something_else
8fsztsew

8fsztsew1#

数据恰好具有有效的tcl列表语法:

set f [open "input.file"]
set data [dict create {*}[read $f]]
close $f

set name [string trim [dict get $data product name]]
dict for {key val} [dict get $data product customers] {
    lappend customers [format "%s{%s}" $key [string trim $val]]
}

set f [open "output.csv" w]
puts $f "product,customers,another_column"
puts $f [join [list $name [join $customers] "something_else"] ,]
close $f

创建输出.csv

product,customers,another_column
thing1,mary{} freddy{} bob{spouse betty},something_else
nlejzf6q

nlejzf6q2#

编辑 * 请参见末尾以生成完整的所需输出 *

这里是regex,可能是任何语言的,在一个字符串中运行整个文件。这是假设在一个客户下只能有一层嵌套,换句话说,bob不能有{ pets { dog } }之类的。
customers截面浸提液含量

/customers\s*{\s* ( (?: [^{]+ {[^}]*} )+ )/x;

然后将换行符+空格折叠为单个空格

s/\n\s+/ /g;

然后从字符串中删除空格,如bob { spouse },但不从mary { }

s/{\s+ ([^}]+) \s+}/{$1}/gx;

如果bob和船员真的可以只是单词字符,那么我们可以使用更好的\w来代替[^{}]
总之,在Perl命令行程序中似乎是所希望的

perl -wE'die "file?\n" if not @ARGV; 
    $d = do { local $/; <> };
    ($c) = $d =~ /customers\s*{\s* ( (?: [^{]+ {[^}]*} )+ )/x; 
    $c =~ s/\n\s+/ /g;          
    $c =~ s/{\s+ ([^}]+) \s+}/{$1}/gx; 
    say $c
' data.txt

对于问题中给出的数据,将打印

mary { } freddy { } bob {spouse betty}

要把每个客户打印在一个单独的行上可以这样做例如

say for split /(?<=\})\s+/, $c;

(to是代码的最后一行)
现在我意识到还有更多的内容需要捕获和打印,如上一段所述。

perl -wE'die "file?\n" if not @ARGV; 
    $d = do { local $/; <> };
    ($n, $c) = $d =~ /name\s*{\s* ([^}]+) \s*} .*?  customers\s*{\s* ( (?: [^{]+ {[^}]*} )+ )/sx; 
    $n =~ s/^\s+|\s+$//g;
    $c =~ s/\n\s+/ /g;
    $c =~ s/{\s+ ([^}]+) \s+}/{$1}/gx; 
    say "product,customers,another_column"
    say "$n,$c,something_else"
' data.txt > output.csv

重定向到output.csv的输出如问题所示。

uklbhaso

uklbhaso3#

仅限于您展示的示例。在GNU awk中,您可以尝试以下awk代码。我们可以在单个GNU awk中完成,我们不需要将您的sed命令的输出传递给任何其他工具。只需将您的Input_file传递给此awk程序即可。

***第一个解决方案:***要获取customers节到}之间的输出,其右括号和值没有开始空格,请尝试以下GNU awk解决方案。

awk -v RS='\n[[:space:]]+customers {[[:space:]]*.*\n[[:space:]]+}' '
RT{
  sub(/^\n[[:space:]]+[^ ]* {[[:space:]]*\n/,"",RT)
  sub(/\n[[:space:]]+}/,"",RT)
  match(RT,/(.*{)[[:space:]]*([^\n]*)(.*)/,arr)
  sub(/^[[:space:]]+/,"",arr[1])
  sub(/\n/,"",arr[2])
  gsub(/\n|^[[:space:]]+/,"",arr[3])
  gsub(/\n[[:space:]]+/,"\n",arr[1])
  gsub(/ {/,"{",arr[1])
  print arr[1] arr[2] arr[3]
}
'   Input_file

输出如下:

mary{ }
freddy{ }
bob{spouse betty}

***第二个解决方案:***若要在值前使用起始空格,请尝试以下代码。

awk -v RS='\n[[:space:]]+customers {[[:space:]]*.*\n[[:space:]]+}' '
RT{
  sub(/^\n[[:space:]]+[^ ]* {[[:space:]]*\n/,"",RT)
  sub(/\n[[:space:]]+}/,"",RT)
  match(RT,/(.*{)[[:space:]]*([^\n]*)(.*)/,arr)
  sub(/\n/,"",arr[2])
  gsub(/\n|^[[:space:]]+/,"",arr[3])
  print arr[1] arr[2] arr[3]
}
'   Input_file

输出如下:

mary { }
        freddy { }
        bob {spouse betty}

***说明:***简单的说明是在GNU awk中将RS(记录分隔符)设置为\n[[:space:]]+customers {[[:space:]]*.*\n[[:space:]]+},以仅匹配所需的匹配项。然后在此awk程序的主块中,根据sub(替换函数)的要求删除所有不必要的(不需要的字符串部分),然后使用match函数和regex (.*{)[[:space:]]*([^\n]*)(.*),其中有3个捕获组,其值存储到一个名为arr的数组中,然后我替换其中的所有换行符/空格,然后使用RT打印当前行的值。

yzckvree

yzckvree4#

以下代码示例演示了所提供示例数据最基本解析器
此代码恢复数据结构,然后可以以任何可能方式使用,例如存储为 CVSJSONYAML 文件
在真实的生活中,输入数据可能会有很大的不同,此代码可能无法正确处理它。
提供的代码仅用于教育目的。

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $data = do { local $/; <DATA> };

$data =~ s/\n/ /g;
$data =~ s/ +/ /g;

say Dumper parse($data);

exit 0;

sub parse {
    my $str  = shift;   
    my $ret;

    while( $str =~ /^(\S+) \{ (\S+) \{ \S+/ ) {
        if( $str =~ /^(\S+) \{ (\S+) \{ ([^}]+?) \{(.+?)\}/ ) {
            $ret->{$1}{$2}{$3} = $4;
            $ret->{$1}{$2}{$3} =~ s/(^\s+|\s+$)//g;
            $str =~ s/^(\S+) \{ (\S+) \{(.+?)\{(.*?)\}/$1 \{ $2 \{/;
        }
        if( $str =~ /^(\S+) \{ (\S+) \{\s*([^{]+?)\s*\}/ ) {
            $ret->{$1}{$2} = $3 if length($3) > 1;
            $str =~ s/^(\S+) \{ \S+ \{\s*[^\}]+\s*\}/$1 \{/;
        }
    }
    
    return $ret;
}

__DATA__
product {
    name { thing1 }
    customers {        
        mary { }
        freddy { }
        bob {
            spouse betty
        }
    }
}

输出量

$VAR1 = {
          'product' => {
                         'customers' => {
                                          'bob' => 'spouse betty',
                                          'freddy' => '',
                                          'mary' => ''
                                        },
                         'name' => 'thing1'
                       }
        };
djp7away

djp7away5#

也许是ed

ed -s file.txt <<-'EOF'
  %s/^[[:space:]]*//
  ?{?;/^}/j
  %s/^\([^\{]*\) \(.*\)$/\1\2 /
  /^customers/+1;/^}/-1j
  s/^/thing1,/
  s/ *$/,someting_else/
  p
  Q
EOF

使用临时文件,写入新文件会更容易一些。

ed -s file.txt <<-'EOF'
  %s/^[[:space:]]*//
  /customers {/+1;/^[[:space:]]*}/w out.txt
  %d
  r out.txt
  ?{?;/^}/j
  %s/^\([^\{]*\) \(.*\)$/\1\2 /
  %j
  s/^/thing1,/
  s/ *$/,someting_else/
  0a
product,customers,another_column
.
  w output.csv
  ,p
  Q
EOF
  • 后者创建两个文件:out.txtoutput.csv
  • 如果不需要stdout输出,请删除,p
lp0sw83n

lp0sw83n6#

这里输入的文件称为“栈”。

#!/bin/sh -x

cat > ed1 <<EOF
/customers/
+1
ka
$
-2
kb
'a,'bW output.txt
q
EOF

ed -s stack < ed1

相关问题