次の種類のコンテンツを含むテキストファイルがあります。
OPERATION_CONTEXT VMD1HTE1A71_ns:.oc.GJ_OAD2 alarm_object 1130 On director: VMD1HTE1A71_ns:.temip.VMD1HTE1A71_director AT Fri 18 Oct 2013 06:56:39 All Attributes
Identifier = 1130
State = Terminated
Problem Status = Closed
Clearance Report Flag = True
Escalated Alarm = False
Close User Identifier = "Auto-Clear"
Termination User Identifier = "Auto-Clear"
Close Time Stamp = Fri 18 Oct 2013 05:01:46
Termination Time Stamp = Fri 18 Oct 2013 05:01:46
Creation Timestamp = Fri 18 Oct 2013 04:37:29
Clearance Time Stamp = Fri 18 Oct 2013 05:01:40
Last Modification Timestamp = Fri 18 Oct 2013 05:01:46
Previous State = Outstanding
Managed Object = Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD "AMS" Node "INGJJMGRJMTSNB0001AG2OLT001"
Target Entities = { Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD "AMS" Node "INGJJMGRJMTSNB0001AG2OLT001" }
Alarm Type = CommunicationsAlarm
Event Time = Fri 18 Oct 2013 05:01:40
Probable Cause = Unknown
Specific Problems = { }
Notification Identifier = 160315
Domain = Domain VMD1HTE1A71_ns:.dm.GJ_OAD2
Alarm Origin = IncomingAlarm
Perceived Severity = Major
Additional Text = "
nativeProbableCause: Attempt Threshold Crossed
osTime: 20131018163727.250+0530
neTime: 20131011174021.0+0530
notificationId: AMS:160315
portNumber:
ftpNumber:
meNm: INGJJMGRJMTSNB0001AG2OLT001
mdNm: AMS
objectType: OT_MANAGED_ELEMENT
aliasValue: MGMT Security
Access:INGJJMGRJMTSNB0001AG2OLT001:IP10.70.6.6.T0.S841 "
Original Severity = Major
Original Event Time = Fri 11 Oct 2013 05:40:21
各行の値(識別子、状態、問題状態など)と各列ヘッダーの下の値(例:1130、終了、終了待機)を含む、次のすべての行の列ヘッダーを使用して、このテキストファイルのCSVファイルを作成したいです。 。 "="のある行では、他のものもCSVファイルに抽出したくありません。
ここで発生するもう1つの問題は、一部のフィールドに追加されたテキストなどの改行があることです。 「追加テキスト列」の下の列の「追加テキスト」値をすべて取得したいと思います。
私はLinux / Unixに初めて触れたので、これを行う方法を見つけることができません。これを行う最良の方法は何ですか?
答え1
もちろん、各レコードに常に同じ数のフィールドがあり、レコード間に何もない場合(あなたの投稿に基づいて仮定が正しいかもしれませんし、正確ではないかもしれません)、awkパスに行くことができます。これにより、列の順序と埋め込まれた改行が維持されます。次の場所にあるとしますparse.awk
。
BEGIN {
RS = "( = |\n\\s+)";
isHeader = 0;
Sep = "\",\"";
Q = "\"";
# WinEOL = "\r"; # enable this if your CSV will be used on Windows
Headers = Fields = Q;
}
function sanitise (Entry) {
gsub(/(^[ "]*|[" \n]*$)/, "", Entry); # Trim leading/trailing double quotes and white space
gsub(/"/, "\"\"", Entry); # Escape double quotes
return Entry;
}
function addField (Field) {
Fields = Fields FieldsSep sanitise(Field);
isHeader = 1;
FieldsSep = Sep;
FieldCounter++
}
function addHeader (Header) {
Headers = Headers HeadersSep sanitise($0);
isHeader = 0;
HeadersSep = Sep;
}
1 == NR { # Special case of first header
addHeader($1);
next;
}
$0 == "\"" { # Fields with newlines
LongField = $0;
LongFieldSep = "";
while (getline > 0) {
LongField = LongField LongFieldSep $0;
LongFieldSep = "\n";
if ($NF ~ /"$/) {
addField(LongField);
next;
}
}
}
{
if (isHeader) {
addHeader($0);
}
else {
addField($0);
}
if (FieldsPerRecord == FieldCounter) {
if (!HeadersPrinted) {
print Headers Q WinEOL;
HeadersPrinted = 1
}
print Fields Q WinEOL;
Fields = FieldsSep = "";
FieldCounter = 0
}
}
FieldsPerRecord
その後、コマンドラインからsetを使用して呼び出すことができます。
$ awk -v FieldsPerRecord=26 -f parse.awk data.csv
これは、LibreOffice Calcが問題なく受け入れるように見える次のCSVエンコーディングデータを生成します。
"Identifier","State","Problem Status","Clearance Report Flag","Escalated Alarm","Close User Identifier","Termination User Identifier","Close Time Stamp","Termination Time Stamp","Creation Timestamp","Clearance Time Stamp","Last Modification Timestamp","Previous State","Managed Object","Target Entities","Alarm Type","Event Time","Probable Cause","Specific Problems","Notification Identifier","Domain","Alarm Origin","Perceived Severity","Additional Text","Original Severity","Original Event Time"
"1130","Terminated","Closed","True","False","Auto-Clear","Auto-Clear","Fri 18 Oct 2013 05:01:46","Fri 18 Oct 2013 05:01:46","Fri 18 Oct 2013 04:37:29","Fri 18 Oct 2013 05:01:40","Fri 18 Oct 2013 05:01:46","Outstanding","Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD ""AMS"" Node ""INGJJMGRJMTSNB0001AG2OLT001","{ Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD ""AMS"" Node ""INGJJMGRJMTSNB0001AG2OLT001"" }","CommunicationsAlarm","Fri 18 Oct 2013 05:01:40","Unknown","{ }","160315","Domain VMD1HTE1A71_ns:.dm.GJ_OAD2","IncomingAlarm","Major","nativeProbableCause: Attempt Threshold Crossed
osTime: 20131018163727.250+0530
neTime: 20131011174021.0+0530
notificationId: AMS:160315
portNumber:
ftpNumber:
meNm: INGJJMGRJMTSNB0001AG2OLT001
mdNm: AMS
objectType: OT_MANAGED_ELEMENT
aliasValue: MGMT Security
Access:INGJJMGRJMTSNB0001AG2OLT001:IP10.70.6.6.T0.S841","Major","Fri 11 Oct 2013 05:40:21"
持ってきたので参考にしてくださいすべてを引用このアプローチは、少なくとも私にはインポート時に驚くほど少ないですが、 でQ = ""
2Sep = ","
行を設定することでこの機能を無効にできます。gsub()
sanitise()
しかし、、私いいえこれが正規表現の問題だと思います。データは固定幅なので、次のようになります。真珠unpack
おそらく最良の方法でしょう。私はこれを見つけることができませんでしたが、誰かがこれを行う方法を見せたいことを確認するための良い機会になることができますunpack
。
修正する
私はPerl Hacker™ではありませんが、次はうまくいくようです。複数行フィールドの内容を想定せずに、フィールドの順序とフィールド内のすべての元の間隔を維持します(ただし、ヘッダーの先行スペースは削除します)。 Perl-Free非専門家の目には美しく見えます。
BEGIN{
our (@headers, @fields);
our $headers_printed = 0;
}
my ($header, $field) = unpack("A36x2A*", $_); # magic!
if ("" eq $header) { # Fields with newlines
$fields[$#fields] .= "\n" . $field;
next;
}
push(@headers, $header =~ s/^\s*//gr);
push(@fields, $field);
if (26 == $#headers + 1) { # Print complete record
printf "%s\n", join ",", @headers unless $headers_printed;
$headers_printed = 1;
printf "%s\n", join ",", @fields;
@fields = @headers = ();
}
ただ電話してください:
$ perl -nf /tmp/parse.pl /tmp/data.txt
Identifier,State,Problem Status,Clearance Report Flag,Escalated Alarm,Close User Identifier,Termination User Identifier,Close Time Stamp,Termination Time Stamp,Creation Timestamp,Clearance Time Stamp,Last Modification Timestamp,Previous State,Managed Object,Target Entities,Alarm Type,Event Time,Probable Cause,Specific Problems,Notification Identifier,Domain,Alarm Origin,Perceived Severity,Additional Text,Original Severity,Original Event Time
1130,Terminated,Closed,True,False,"Auto-Clear","Auto-Clear",Fri 18 Oct 2013 05:01:46,Fri 18 Oct 2013 05:01:46,Fri 18 Oct 2013 04:37:29,Fri 18 Oct 2013 05:01:40,Fri 18 Oct 2013 05:01:46,Outstanding,Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD "AMS" Node "INGJJMGRJMTSNB0001AG2OLT001",{ Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD "AMS" Node "INGJJMGRJMTSNB0001AG2OLT001" },CommunicationsAlarm,Fri 18 Oct 2013 05:01:40,Unknown,{ },160315,Domain VMD1HTE1A71_ns:.dm.GJ_OAD2,IncomingAlarm,Major,"
nativeProbableCause: Attempt Threshold Crossed
osTime: 20131018163727.250+0530
neTime: 20131011174021.0+0530
notificationId: AMS:160315
portNumber:
ftpNumber:
meNm: INGJJMGRJMTSNB0001AG2OLT001
mdNm: AMS
objectType: OT_MANAGED_ELEMENT
aliasValue: MGMT Security
Access:INGJJMGRJMTSNB0001AG2OLT001:IP10.70.6.6.T0.S841 ",Major,Fri 11 Oct 2013 05:40:21
を使用する方が良いかもしれませんが、そのText::CSV
仕組みを理解することに興味がありますunpack
。固定幅データの正規表現よりも読みやすく強力に見えます。
答え2
または、Perlの正規表現サブルーチンを使用できます。
my $grammar = qr!
( ?(DEFINE)
(?<Identifier> [^=\n]+ )
(?<Statement>
(?: # Begin alternation
" #Opening quotes
[^"]+? # Any non-quotes (including a new line)
" # Closing quotes
| [^\n]+ # Or a single line
) # End alternation
)
)
!x;
my $file = do { local $/; <> }; #Slurp file named on command line
my %columns;
while( $file =~
m{ ((?&Identifier))[\t ]*=[ \t]*((?&Statement)) $grammar}xgc )
{
my ($header,$value) = ($1,$2);
# Remove leading spaces and quote variable if it contains commas:
for($header,$value) { s/^\s+//mg; /,/ and s/^|$/"/g }
# Substitute \n with \\n to make multi-line values single-line:
for($value) { chomp; s/\n/\\n/g }
$columns{$header}=$value
}
print join "," => sort keys %columns; # Print column headers
print "\n";
print join "," => map { $columns{$_} } sort keys %columns; # Column content
print "\n";
次のように呼び出します。
[user@host]$ /path/to/script.pl /path/to/file.txt
テーブルをCSV形式で標準出力として印刷します。
これは、複数行ステートメント"
に始まりと終わりを除いて二重引用符()が含まれていないと仮定します。
答え3
さて、あまり良くありませんが、好きなようにしてください。私は上記のファイルをインポートして解析し、Text::CSV
このモジュールを使用してCSV形式に変換するスクリプトをPerlで書いた。
スクリプト
#!/usr/bin/env perl
use Text::CSV;
open(my $fh, "<data.txt");
@lines = <$fh>;
close ($fh);
my (%csv, $name, $val);
foreach my $line (@lines) {
if ($line =~ m/=/) {
chomp($line);
$line =~ s/^\s+//g;
($name, $val) = split(/ = /, $line);
$val =~ s/^"$//;
$csv{$name} = $val;
} else {
$line =~ s/^\s+//g;
$line =~ s/\s+$/\\n/g;
$line =~ s/ "\\n$//;
$csv{$name} .= $line;
}
}
my @vals;
foreach my $i (sort keys %csv) {
push(@vals, $csv{$i});
}
my $ccsv = Text::CSV->new();
$ccsv->combine(sort keys %csv);
$ccsv->parse($ccsv->string());
print $ccsv->string() . "\n";
$ccsv->combine(@vals);
$ccsv->parse($ccsv->string());
print $ccsv->string() . "\n";
はい
次のように実行してみてください。
$ ./csv.pl
"Additional Text","Alarm Origin","Alarm Type","Clearance Time Stamp","Close Time Stamp","Creation Timestamp",Domain,"Event Time","Last Modification Timestamp","Managed Object","Notification Identifier","Original Event Time","Original Severity","Perceived Severity","Previous State","Probable Cause","Specific Problems","Target Entities","Termination Time Stamp"
"nativeProbableCause: Attempt Threshold Crossed\nosTime: 20131018163727.250+0530\nneTime: 20131011174021.0+0530\nnotificationId: AMS:160315\nportNumber:\nftpNumber:\nmeNm: INGJJMGRJMTSNB0001AG2OLT001\nmdNm: AMS\nobjectType: OT_MANAGED_ELEMENT\naliasValue: MGMT Security\nAccess:INGJJMGRJMTSNB0001AG2OLT001:IP10.70.6.6.T0.S841",IncomingAlarm,CommunicationsAlarm,"Fri 18 Oct 2013 05:01:40","Fri 18 Oct 2013 05:01:46","Fri 18 Oct 2013 04:37:29","Domain VMD1HTE1A71_ns:.dm.GJ_OAD2","Fri 18 Oct 2013 05:01:40","Fri 18 Oct 2013 05:01:46","Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD ""AMS"" Node ""INGJJMGRJMTSNB0001AG2OLT001""",160315,"Fri 11 Oct 2013 05:40:21",Major,Major,Outstanding,Unknown,"{ }","{ Alcatel_5529OAD VMD1HTE1A71_ns:.OAD2 MD ""AMS"" Node ""INGJJMGRJMTSNB0001AG2OLT001"" }","Fri 18 Oct 2013 05:01:46"
あなたのコメントや実行に問題がある場合はお知らせください。お客様の要件を満たしている場合は、どのように動作するかについての詳細を入力します。