どのサーバーに特定のファイルと所有権があるかを知らせるcsvファイルを生成したいと思います。これは私が得る生の出力です。
server01,server02,server03,owner,/etc/file1
server04,owner,/etc/file2
server05,server06,owner,/etc/file3
サーバー名間のカンマをWindows形式の改行(CF + LR)に変更し、その間に引用符を追加して、CSVに同じボックスにすべてのサーバーを表示させたいと思います。
希望の出力:
"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3
これを達成するためにsedを使用する方法は?
答え1
,owner,
フィールドにラップする残りのテキストがすべてある場合:
GNUの使用sed
:
sed -E ':1; s/,(.*,owner,)/\r\n\1/; t1; s/(.*)(,owner,)/"\1"\2/' file
そしてperl
:
perl -pe 's{.*?(?=,owner,)}{q(") . ($& =~ s/,/\r\n/gr) . q(")}e' file
最後の2つのフィールドを除くすべての項目がある場合:
GNUの使用sed
:
sed -E ':1; s/(.*),(.*,.*,)/\1\r\n\2/; t1; s/(.*),/"\1",/' file
そしてperl
:
perl -pe 's{(.*)(?=,.*,)}{q(") . ($& =~ s/,/\r\n/gr) . q(")}e' file
または、Text::CSV
正しいCSVの解析とフォーマットのためにPerlモジュールを使用してください。
perl -MText::CSV -e '
$csv = Text::CSV->new({binary => 1, decode_utf8 => 0, eol => $" = "\r\n"});
while ($row = $csv->getline(STDIN)) {
if (($last = $#{$row}) > 1) {
$csv->print(STDOUT, ["@$row[0..$last-2]", @$row[$last-1..$last]]);
} else {
$csv->print(STDOUT, $row);
}
}' < file
ファイルがBOMでUTF-16またはUTF-8でエンコードされている場合(Microsoftファイルでは前例のない作業)、調整する必要があります(この方法については参考資料を参照)、何らかの方法でエンコードする必要があります。通常の入力を処理できるようにperldoc Text::CSV
フォーマットを再指定する方法です。<file dos2unix | ... | unix2dos
答え2
私はこれをしませんsed
。私は次のものを使用しますperl
(またはおそらくawk
- しかし、Perlの組み込み機能を使用する代わりに私自身のpop()
関数を書く必要があります):join()
$ perl -F, -lane '$file = pop @F; $owner = pop @F;
print join(",", "\"" . join("\r\n", @F) . "\"", $owner, $file)' input.csv
"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3
まず、配列から最後の2つの要素(所有者とファイル名)を削除し@F
(オプションがカンマをフィールド区切り文字として使用するように指定するため)、@F
各入力行に対して自動的に生成されます - awkが入力を自動的に分割する方法に似ています)追加します。変数 sum に保存されます。 。-a
-F,
$file
$owner
"\"" . join("\r\n", @F) . "\""
@Fの各要素はCR + LF文字で区切られ、文字列全体が二重引用符で囲まれた文字列を構成します。
$owner
文字列は、およびと連結して(カンマで)$file
印刷されます。
答え3
各レコードの最後の2つのフィールドまで(含まれていない)、次のすべてのフィールドと結合された最初のカンマ区切りフィールドを引用して、引用符付きCSVフィールドを作成しようとしています。次に、結合フィールドに含まれるコンマをCR + LFに置き換える必要があります。
ファイルの各行の内容を置き換えて、2番目のカンマの後に二重引用符を挿入し、行を反転させ、先頭に二重引用符を挿入することで、簡単にこれを行うことができます。
$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/'
"server01,server02,server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05,server06",owner,/etc/file3
正しく参照されているヘッダーのないCSVファイルがあるので、次のものを使用できます。ミラー(mlr
;ツール具体的に構造化データ(CSVなど)を処理するには、最初のフィールドのすべてのコンマをCR + LFに置き換えます。
$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N put '$1 = gsub($1, ",", "\r\f")'
server01
server02
server03,owner,/etc/file1
server04,owner,/etc/file2
server05
server06,owner,/etc/file3
Unixシステムでは、フィールドとレコード区切り文字はこのデータセットのフィールドに含まれていないため、フィールドを引用する必要はありません。別々の呼び出しで、各レコードから2番目のフィールドを抽出してそれを表示できますmlr
。
$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N put '$1 = gsub($1, ",", "\r\f")' | mlr --csv -N cut -f 2
owner
owner
owner
sed
最後のコマンド出力の元の引用を維持するには、次のようにします--quote-original
。
$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N --quote-original put '$1 = gsub($1, ",", "\r\f")'
"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3
ただし、理想的には、後処理ステップでフィールドを変更するのではなく、最初からフィールドが正しくなるように破損したCSV出力を生成するコードを変更する必要があります。
Millerのみを使用:
mlr --nidx --fs comma put '
for (var i=2; NF > 3; i=i+1) {
$1 .= "\r\f" . $[i];
unset $[i]
}
$1 = "\"" . $1 . "\""' file
これは、ファイルを単純なカンマで区切られ、暗黙的に索引付けされたテキストファイルとして読み取ります。次に、CR + LFを区切り文字として使用して、2番目と2番目のフィールドを最初のフィールドの末尾に追加し、3つのフィールドのみが残るまで各追加フィールドを削除します。次に、最初のフィールドを明示的に参照します。