その人が過去1年以内に生まれていない場合は、person.csv(下)から行を削除しようとします。
データセット1:
"Index","User Id","First Name","Last Name","Date of birth","Job Title"
"1","9E39Bfc4fdcc44e","new, Diamond","Dudley","06 Dec 1945","Photographer"
"3","32C079F2Bad7e6F","Ethan","Hanson","08 Mar 2014","Actuary"
"2","aaaaaaa, bbbbbb","Grace","Huerta","21 Jan 2023","Visual merchandiser"
したがって、予想される出力は次のようになります(最後の行は1年以内に削除されました)。
"Index","User Id","First Name","Last Name","Date of birth","Job Title"
"1","9E39Bfc4fdcc44e","new, Diamond","Dudley","06 Dec 1945","Photographer"
"3","32C079F2Bad7e6F","Ethan","Hanson","08 Mar 2014","Actuary"
私はawkを使って次のことをしようとしています。
awk -F , '{print $5 ....}' person.csv > output.csv
ただし、各日付行を(今日から1年を引いた値)と比較する方法はわかりません。
データセット2:時には二重引用符で囲まれたフィールドに二重引用符があります(例:line1 field4)。
"Index","User Id","First Name","Last Name","Date of birth","Job Title"
"1","9E39Bfc4fdcc44e","new, Diamond","Dudley (aka "dud")","03 Oct 2023","Photographer"
"3","32C079F2Bad7e6F","Ethan","Hanson","03 Dec 2022","Actuary"
"2","aaaaaaa, bbbbbb","Grace","Huerta","21 Jan 2023","Visual merchandiser"
「sed」がこれを行うことができれば、私もそれについて開いています。どんな助けでもお願いします。ありがとうございます!
ベストアンサー1
仮定:
- すべての列/フィールドは二重引用符で囲まれています。
- 二重引用符はデータの一部として表示されません。それ以外の場合は、
-F'"'
フィールド区切り文字として基本文字以外の文字が必要です。 - OP(オペレーティングシステム)は
date
この-d
パラメータをサポートします(たとえば、16 Sep 2023
OPシステムの「今日」date -d '-1 year' '+%Y%m%d'
が生成される場合20220916
) - OPでは、締め切りは何でも可能であると述べたので(例:-1年、-7日など)、(オペレーティングシステム)を使用して
date
特定の形式で期限を作成しますYYYYMMDD
(そうでない場合は、コードを追加するする必要があります)awk
。 「-1年」、「-7日」など様々な条件を処理できます。)
離れてawk
いる:
cutoff=$(date -d '-1 year' '+%Y%m%d') # change '-1 year' to the desired condition;
# alternatively: manually set to the desired date (in YYYYMMDD format)
awk -v cutoff="${cutoff}" -F'"' ' # set awk variable "cutoff" to the value of the OS variable of the same name
# field delimiter is double quotes; this means data fields are even-numbered (eg, 5th field is the 10th "-delimited field)
BEGIN { mlist="JanFebMarAprMayJunJulAugSepOctNovDec" }
NR>1 { split($10,a,/[[:space:]]+/) # split 5th data field on spaces; a[1]=day a[2]=month a[3]=year
m=sprintf("%02d", ( (index(mlist,a[2])+2) /3) ) # convert 3-letter month to 2-digit month
if ( a[3] m a[1] > cutoff) next # if new date is greater than the cutoff then skip to the next line of input
}
1 # print the current line
' person.csv
これで以下が生成されます。
"Index","User Id","First Name","Last Name","Date of birth","Job Title"
"1","9E39Bfc4fdcc44e","new, Diamond","Dudley","06 Dec 1945","Photographer"
"3","32C079F2Bad7e6F","Ethan","Hanson","08 Mar 2014","Actuary"
パフォーマンスの観点...
この回答には単一のオペレーティングシステム呼び出しが必要で、date
1つのファイル記述子を開く/閉じる必要があります(出力が別のファイルにリダイレクトされる場合は2つ)。
date
Gillesの答えには、各入力行に対するオペレーティングシステムの呼び出しが必要です。そして各呼び出しに対してファイル記述子を開閉するのに費用がかかるオーバーヘッドですdate
。
テスト実行:
100K line file # per comment from OP
GNU awk 5.1.0
GNU date 8.32
Ubuntu 20.04
i7-1260P
この答え:
real 0m0.198s <<< 546 times faster
user 0m0.198s
sys 0m0.000s
ザイルズの答え:
real 1m48.229s <<<
user 1m30.598s
sys 0m23.999s
両方の実行の出力はファイルに保存されます。diff
aは、両方の出力ファイルに違いがないことを示しています(つまり、両方の答えは同じ結果セットを生成します)。
この場合、OPはすべてのフィールドが二重引用符で囲まれていることを示しています。
一部のフィールドを二重引用符で囲むことができない場合は、ペアへGNU awk's 'FPAT'
の単一の呼び出しのみを使用して実行できますdate
。たとえば、次のようになります。
cutoff=$(date -d '-1 year' '+%Y%m%d')
awk -v cutoff="${cutoff}" '
BEGIN { FPAT="([^,]+)|(\"[^\"]+\")"
mlist="JanFebMarAprMayJunJulAugSepOctNovDec"
}
NR>1 { f5=$5
gsub(/"/,"",f5) # strip double quotes from 5th data field
split(f5,a,/[[:space:]]+/) # change from 10th field to 5th field
m=sprintf("%02d", ( (index(mlist,a[2])+2) /3) )
if ( a[3] m a[1] > cutoff) next
}
1
' person.csv
上記と同じテスト基準を使用して、この回答の実行時間は次のとおりです。
real 0m0.861s <<<
user 0m0.850s
sys 0m0.009s
FPAT
(代わりに-F'"'
)入力解析に基づくと、実行時間は約4倍になりますが、それでも108秒よりはるかに高速です。