複数のcsvファイルで特定の文字列を検索および置換する

複数のcsvファイルで特定の文字列を検索および置換する

次の形式のCSVファイルが複数あります。

"TIMESTAMP",col2,col3,col4
"yyyy-mm-dd HH:mm",20,19,17

私は最終的に次のように-交換したいと思います。/

TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

次のコマンドを使用してファイルを添付しましたが、何も起こりません。

find -name '*.csv' -exec awk '{gsub(/-/, "/",$1)}' '{}' \;

私は何を見逃していますか?助けてください

答え1

find . -name '*.csv' -type f -size +10c -exec perl -pi -e '
  s{^(\d\d\d\d)-(\d\d)-(\d\d)\b}{$1/$2/$3}' {} +

-行の先頭のタイムスタンプのsだけが置き換えられ、他のすべての-イベントは無視されます。

.csvそのタイムスタンプを含まないファイルも含め、すべてのファイルを置き換えます。これを防ぐには、GNUを使用してgrep次のことを実行できます。

grep -rlPZ --include='*.csv' '^\d\d\d\d-\d\d-\d\d\b' . |
  xargs -r0 perl -pi -e '
  s{^(\d\d\d\d)-(\d\d)-(\d\d)\b}{$1/$2/$3}' {} +

答え2

コマンドはawk実際に必要な変更を実行しますが(カンマ区切りフィールドではなく最初のスペースで区切られたフィールドを変更する場合を除く)、印刷を指示しなかったため印刷されません。

$ cat  file.csv
TIMESTAMP,col2,col3,col-4
yyyy-mm-dd HH:mm,20,19,17
$ awk '{gsub(/-/, "/",$1)}' file.csv 
$ 

上記のように、コマンドに出力はありません。比較:

$ awk -F, -v OFS=, '{gsub(/-/, "/", $1); print}' file.csv 
TIMESTAMP,col2,col3,col-4
yyyy/mm/dd HH:mm,20,19,17

しかし、これは単にすべてのファイルの内容を標準出力として印刷するので、望ましい結果ではないかもしれません。実際のファイルを変更するには、次のようにします。

$ perl -i -F, -lane '$F[0] =~ s|-|/|g; print join ",",@F' file.csv 
$ cat file.csv
TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

場合によっては、-i.bak拡張子を持つ元のファイルのコピーを作成して使用することをお勧めします。.bakその後、次のようにコマンドに統合できます。

find . -name '*.csv' -type f -exec perl -i.bak -F, -lane '
  $F[0] =~ y|-|/|; print join ",", @F' '{}' +

または、GNU awk()が存在し、gawk現在の作業ディレクトリに名前infileまたはファイルが含まれていないことを保証できるinfile.awk場合は、次のようにします。

find . -name '*.csv' -type f -exec gawk -F, -v OFS=, -i inplace '
  {gsub(/-/, "/",$1); print}' '{}' +

答え3

質問に示されている入力例と期待される出力をあなたが言ったものとマージしてください。コメントタイムスタンプ値に実際に何が起こりますか?

$ cat foo.csv
"TIMESTAMP",col2,col3,col4
""yyyy-mm-dd HH:mm"",20,19,17

次に、GNU awkを使用して以下を実行します-i inplace

$ find . -name 'foo.csv' -exec awk -i inplace '{gsub(/"/,""); gsub(/-/,"/"); print}' {} +

$ cat foo.csv
TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

あるいは、GNU awkスクリプト(for -i)を次のGNU sedスクリプトに置き換えることもできます。

sed -i 's:"::g; s:-:/:g'

"sまたはsを他の場所で置き換えたくない場合-、参照フィールドに"s、s、または改行文字がない場合は、,awkスクリプトを次のように変更します。

BEGIN{FS=OFS=","} {gsub(/"/,"",$1); gsub(/-/,"/",$1); print}

答え4

使用幸せ(以前のPerl_6)

簡単な方法:

~$ raku -pe 's:g{  \w**4  <( (\-)  (\w**2)  (\-) )>  \w**2 } = "/$1/";'  file

CSVコンテンツ検証:

~$ raku -MText::CSV -e 'my @a = csv(in => $*IN, sep => ",");   \
                        @a>>.[0] = @a>>.[0].map:               \
                        *.subst(:global, / \w**4 <( (\-) (\w**2) (\-) )> \w**2 /, {"/$1/"} );  \
                        csv(in => @a, out => $*OUT, sep => ",");'  < file

以下は、Perlシリーズのプログラミング言語であるRakuで書いた答えです。最初の答えはおなじみのs///代替イディオムを使用していますが、ここでRakuは新しいフォーマットオプションを追加しましたs{original} = "replacement"。文字/数字とダッシュ(ハイフン)の正しい組み合わせに一致するアイテムを探すRakuの<(…)>キャプチャフラグは、目的のブロックの外側のすべてのアイテムを削除するために使用され、交換時に変更されます。

2番目の答えは、RakuのText::CSVモジュールを使用してCSV入力/出力を検証します。サンプルデータのみを使用最初の行置き換えられます(この@a>>.[0]慣用語はデータの最初の列にのみ適用されます)。

入力例:

"TIMESTAMP",col2,col3,col4
"yyyy-mm-dd HH:mm",20,19,17

出力例(最初のコード例):

"TIMESTAMP",col2,col3,col4
"yyyy/mm/dd HH:mm",20,19,17

出力例(2番目のコード例、CSV出力確認):

TIMESTAMP,col2,col3,col4
"yyyy/mm/dd HH:mm",20,19,17

Text::CSVこれを変更する必要がある場合、Rakuモジュールには出力列を参照するためのさまざまなオプションがあります(デフォルトは空白を含む列単位要素を参照することです)。シェルのグロービングに頼らずに、コードの本文から入力を取得することもできます。csv(in => $*IN, sep => ",");コード部分を次のように変更してください。

csv(in => "path/to/file", sep => ",");


https://raku.land/zef:Tux/Text::CSV
https://docs.raku.org/routine/dir
https://raku.org

関連情報