ファイルのために悩んでいます。通常、ホストごとに1行を定義する必要があります。しかし、時々、誰かがいくつかのフィールドを別の行に分割することがあります。例は次のとおりです。
"host1","host1","linux
server",""
"host2","host2","linux server",""
今、私はこの問題を解決する方法(bashでより良い方法)を見つけたいと思います。
"host1","host1","linux server",""
"host2","host2","linux server",""
各フィールドは二重引用符で囲む必要があります。そうでない場合は、\n
aが挿入されてからそれを削除して、行ごとに常に4つのフィールドがあることを意味します。
説明を複数行に分けることができます。たとえば、次のようになります。
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
awk
私は次のようないくつかのアプローチを試しました。
awk 'BEGIN {ORS=""; RS="\"\n\""; FS="\",\""; OFS="\",\""} {if (NF == 3) print "\"" $1 "\"," $2 "\"," $3 "\"\n"; else printf "%s", $0} END {print ""}' /tmp/ngr4
しかし、私は成功しなかったし、この強力なツールは限界に達し始めました。
答え1
使用ミラー( mlr
)、さまざまな構造化文書形式のCSVサポートを備えた多目的処理ユーティリティで、すべてのフィールドでスペースをクリーンアップするために使用されます。
$ cat file
"host1","host1","linux
server",""
"host2","host2","linux server",""
$ mlr --csv -N clean-whitespace file
host1,host1,linux server,
host2,host2,linux server,
データをfile
ヘッダーなしのCSVレコードに読み込み、適用します。clean-whitespace
仕事すべて。このclean-whitespace
操作は、各フィールド値から横のスペースを切り取り、連続したスペース文字を単一のスペースに結合します。
に変更改行を空白にのみ変更してください、短いステートメントを使用してフィールドを繰り返すことができます。put
表現する:
$ mlr --csv -N put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
host1,host1,linux server,
host2,host2,linux server,
gssub()
機能これはAwkのように動作しますgsub()
が、クエリパラメータを正規表現として処理しません(Millerも同様ですgsub()
)。
必須ではなく、フィールドに引用符を付ける必要があると思われる場合(フィールド値に必要な場合はMillerが自動的に引用符を追加します)、オプションmlr
と一緒に使用してください--quote-all
。
$ mlr --csv -N --quote-all clean-whitespace file
"host1","host1","linux server",""
"host2","host2","linux server",""
$ mlr --csv -N --quote-all put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
"host1","host1","linux server",""
"host2","host2","linux server",""
答え2
最後にやりたいことは、bashでこれを行うことです。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?。
今、あなたが望むものを「文字のすぐ後ろに現れない限り、改行を削除してください"
」と表現できれば、次のことができます。
perl -pe 's/(?<!")\n/ /g' file
(?<!")\n
前にない改行文字と一致します"
。したがって、次の入力例が与えられます。
$ cat file
"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
上記のコマンドは以下を提供します。
$ perl -pe 's/(?<!")\n/ /g' file
"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""
しかし、実際にmlr
これが最善の方法です。
答え3
使用幸せ(以前のPerl_6)
@terdonの優れたPerl回答からインスピレーションを得ました。
~$ raku -ne '/ <!after \" > $/ ?? print "$_ " !! put $_;' file
以下はRaku(別名Perl6)で書かれた答えです。 Rakuには、よく知られているいくつかの慣用語をクリーンアップしようとする新しいUnicode認識正規表現エンジンがあります。したがって、(例えば)「Y not after X」否定的な振り返り慣用語は、<!after X > Y
Rakuで<?after … >
肯定的な振り返りを意味し、<!after … >
否定的な振り返りを意味します。
\n
Rakuは行ターミネーターの処理を標準化しているので(改行はデフォルトで自動的に切り捨てられます)、Rakuの三項演算子を使用してパターンを検出できます。テスト ??
本物 !!
間違った次に、print
(\n
改行を追加せずに)またはput
(\n
テキストの末尾に改行を追加)を使用して出力します。
入力例:
"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
出力例:
"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""
その他 Raku ソリューション
RakuText::CSV
モジュールの使用:
Rakuエコシステムの適切なCSVパーサー(モジュール)を使用するのは非常に簡単です(参照:https://raku.land/?q=CSV)。これはRFC 4180準拠を確認し、標準化されたCSV出力だけでなく、幅広いカスタマイズを提供できます。
以下では、RakuのText::CSV
モジュールはOPの入力を見事に解析し、\n
改行を取り除いた後、基本的に内部スペースを持つ列だけを二重引用符で囲んだ列(最初の答え)に出力します。 2番目の答えは1行ずつ読み、最初の答えと同じ答えを生成します。
ファイル全体をメモリに読み込みますcsv()
。デフォルト出力は次のとおりです。
~$ raku -MText::CSV -e 'my @a = csv(in => "/path/to/file", sep => ",");
@a = @a>>.map( *.trans: "\n" => " ");
csv(in => @a, out => $*OUT, sep => ",");'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",
1行ずつ読み、出力を「手動で」引用します。
~$ raku -MText::CSV -e 'my $fh = "/path/to/file"; my $io = open $fh, :r, :!chomp;
my $csv = Text::CSV.new; my @data;
while $csv.getline($io) -> $row {
@data.push: $row.map: *.trans: "\n" => " "; };
put $_.join(",") for @data>>.map({ / \s / ?? (q["] ~ $_ ~ q["]) !! $_ });'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",
https://docs.raku.org/言語/operators#infix_??_!!
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md#embedded-newlines
https://raku.org
答え4
不要な改行文字が多い場合でも、二重引用符はすべて一致し、フィールド区切り文字はすべて存在すると仮定します。この場合、次のコマンドを使用できます。
$ sed '/^"/! s/^/ /' infile | tr -d '\n' | sed '-e s/"/"\n/'{8..1000..8}
"host1","host1","linux server centos",""
"host2","host2","linux server",""
どこ:
$ cat infile
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
これは望ましくない改行が発生する可能性があるすべての場所で機能します。
中括弧内の数字1000は任意の大きな数字であり、入力ファイルの合計文字数より大きくなければなりません。
入力行に先頭/末尾のスペースが含まれていると思われる場合は、まずスペースを削除してください。たとえば、次のコマンドを使用します。awk 'NF{$1=$1}1' infile