BashスクリプトでCSVファイルの日付を変更する

BashスクリプトでCSVファイルの日付を変更する

現在、次の履歴を含むファイルがあります。

D20211011,S0519,306668,1
D20211004,S1600,306668,1
D20211009,S1604,306668,1
D20211010,S1605,306668,1
D20211006,S1610,306668,1
D20211011,S1611,306668,1

現在の日付があると仮定すると、20211011日付が現在の日付より小さい行にのみ変換を適用し、過去の日付を持つ行を現在の日付に更新する必要があります。

上記の共有例では、2行から5行に変換する必要があります。

D20211004,S1600,306668,1 -> D20211011,S1600,306668,1
D20211009,S1604,306668,1 -> D20211011,S1604,306668,1
D20211010,S1605,306668,1 -> D20211011,S1605,306668,1
D20211006,S1610,306668,1 -> D20211011,S1610,306668,1

答え1

あなたの場合、日付がISOスタイルで提供されるという利点があります。つまり、日付を整数値として解釈し、算術演算子(および)を使用して簡単に比較できますが、<正しい順序を生成できます=>

したがって、次のawkプログラムを使用できます。

awk -v cur="20211011" 'BEGIN{FS=OFS=","} {ldate=substr($1,2); if (ldate<cur) $1="D" cur} 1' input.csv

現在の日付はawk変数として定義されますcur。最初は、入力と出力のフィールド区切り文字がに設定されます,。各行について、行の日付は、その行のフィールド 1 から最初の文字を削除することによって決定されます。結果 "整数" が小さい場合、curと の内容を連結してフィールドを上書きします。ルールブロックの外側にある「散らばった」コンテンツのように見えることは、可能な修正を含む現在の行を印刷するように指示します。Dcur1awk

答え2

努力するawk

awk -v today=$(date +%Y%m%d) '
    BEGIN{FS=OFS=","}
    substr($1,2)<today{$1="D"today;}
1' file
  • -v today=$(date +%Y%m%d)現在の日付を含む変数を設定します。
  • BEGIN{FS=OFS=","}入力(FS)と出力(OFS)フィールドの区切り文字を設定します。
  • substr($1,2)<today最初のフィールドを切り取り、D現在の日付と比較します。
  • $1="D"today;最初のフィールドを現在の日付に置き換えます。
  • 1常に true と評価してラインを印刷します。

答え3

$ awk -v d='D20211011' 'BEGIN{FS=OFS=","} $1<d{$1=d} 1' file
D20211011,S0519,306668,1
D20211004,S1600,306668,1
D20211009,S1604,306668,1
D20211010,S1605,306668,1
D20211006,S1610,306668,1
D20211011,S1611,306668,1

$ awk -v d="$(date +'D%Y%m%d')" 'BEGIN{FS=OFS=","} $1<d{$1=d} 1' file
D20211012,S0519,306668,1
D20211012,S1600,306668,1
D20211012,S1604,306668,1
D20211012,S1605,306668,1
D20211012,S1610,306668,1
D20211012,S1611,306668,1

答え4

Raku(以前のPerl_6)の使用

raku -pe 's/ ^^ D <(\d*?)> \, /20211011/;'

@StéphaneChazelasがOPに関するコメントで指摘したように、「将来の日付」が最初の列に表示されるかどうかはわかりません。それ以外の場合は、s///上記のRakuコードが実行する簡単な置き換えで十分です(数値比較に関係なく、見つかったすべての日付番号を置き換えます< = >)。

< = >ただし、最初の列を数値比較に基づいて値に更新する場合は、s///演算子の代替部分にRaku三項演算子を含むブロックを実行する次のRakuコードを使用できます。

raku -pe 'my Int $d=20211011; s/ ^^ D (\d*?) \, /D{$0 < $d ?? $d !! $0},/;'

入力例:

D20211011,S0519,306668,1
D20211004,S1600,306668,1
D20211009,S1604,306668,1
D20211010,S1605,306668,1
D20211006,S1610,306668,1
D20211011,S1611,306668,1

出力例(上記のRakuコード例):

D20211011,S0519,306668,1
D20211011,S1600,306668,1
D20211011,S1604,306668,1
D20211011,S1605,306668,1
D20211011,S1610,306668,1
D20211011,S1611,306668,1

2番目のRakuコード例では、精度をさらに確認するために変数の$d種類が制限されていますInt。 @AdminBeeが指摘したように、最初の列に< = >演算子と比較してまだ正しい結果を提供できるISO日付が含まれていることは幸運です。

上記のRakuコードの(簡単な)2行目に関連して有効な日付を確認するためのキャプチャの確認がないことに注意する必要があります$0(例:13月または32日が発生しないことを確認)。不完全な日付(月/日など)を除外するコードはありません。フォントなし年)。

OTOHは、Rakuの組み込みサポートDateDateTimeオブジェクトを使用して適切な日付検証を追加するのが比較的簡単です(追加のモジュールは不要、以下の例とリンク)。

$ echo "2020-02-29" | raku -ne '.Date.raku.say'
Date.new(2020,2,29)

$ echo "2021-02-29" | raku -ne '.Date.raku.say'
Day out of range. Is: 29, should be in 1..28
  in block <unit> at -e line 1

https://docs.raku.org/言語/temporal#index-entry-Date_and_time_functions
https://raku.org

関連情報