その人が過去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秒よりはるかに高速です。
答え2
GNUの使用アッそしてGNU日付1年の条件:
awk -v epoch1y=$(date -d '1 year ago' +%s) '
BEGIN{FPAT="([^,]*)|(\"[^\"]+\")"}
NR>1{
cmd="date -d " $5 " +%s"
epoch=( (cmd | getline line) > 0 ? line : "N/A")
close(cmd)
if (epoch > epoch1y) next
}
1
' 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"
説明する
コードは明確で簡潔で読みやすくする必要があります。
明らかでない部分はFPAT
特殊変数(内容別に分ける)、単に次のより多くのユースケースを処理できます-F,
。
一部のプログラムは、二重引用符の間に新しい行を含むCSVデータをエクスポートします。 gawkはこの問題を解決する方法を提供しません。 CSVデータの公式仕様が存在しますが、まだやるべきことはありません。FPATメカニズムは、ほとんどの状況に対してエレガントなソリューションを提供します。、Gawk開発者はこれに満足しています。
これはシェル命令を実行し、後で変数を入力するためのgetline
ヒントです。awk
getline
1
最後にはを意味するので、特定のtrue
条件true
ではデフォルトでprint
現在の行を意味します。
通貨に関する文書close()
:https://www.gnu.org/software/gawk/manual/html_node/Close-Files-And-Pipes.html
epoch
Unix タイムスタンプ: 後に経過した秒数1/1/1970
。
答え3
CSVの引用規則を理解していないため、awk
これを行うにはCSV認識ツールを使用することをお勧めします。
CSV処理ツールの使用csvkit:
$ csvsql -I --query "SELECT * FROM file WHERE \`Date of birth\` <= date('now', '-1 year')" file
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
これは、日付の解析と日付の計算をデータベースバックエンド(SQLite)に委任します。
出力は、参照する必要があるフィールドのみを参照します。引用したい場合みんなフィールドに結果をcsvformat -U1
(他のcsvkitツール)に渡します。
編集:更新された質問で誤って参照されたフィールドは"Dudley (aka "dud")"
csvkitツール(ここでは表示されません)によって変換されます。"Dudley (aka dud"")"""
csvkitツールなし、SQLiteのみを使用:CSVファイルとクエリから直接データをロードします。 SQLiteにはCSVをサポートするリーダーとライターが含まれているため、データが適切に参照されることを確認できます。
rm -f data.db
sqlite3 data.db \
'.headers on' \
'.mode csv' \
'.import file mytable' \
"SELECT * FROM mytable WHERE \`Date of birth\` <= date('now', '-1 year')"
私のシステム(ファンなしの「Intel(R)Core(TM)i5-4300U CPU @ 1.90GHz」)では、100,000レコードに約0.75秒かかり、そのうち0.3秒はSQLiteデータベースを構築するのにかかります。 1Mレコードの場合は7秒未満かかり、そのうち4秒はデータベースの構築にかかります。 10M行の場合、操作には約1分15秒かかり、そのうち45秒はデータベースの構築に使用されます。
編集:更新された質問で誤って参照されたフィールドは、"Dudley (aka "dud")"
SQLite CSVリーダー/作成者によって正しく参照されたフィールド(ここでは表示されません)に変更されます。"Dudley (aka ""dud"")"
答え4
IFS=$'\n'
lyear_old_sec=$(date -d "1 year ago" +%s)
echo '"Index","User Id","First Name","Last Name","Date of birth","Job Title"'
for i in $(cat sup.txt|awk 'NR>1')
do
dat_de=$(echo $i | awk -F "," '{print $(NF-1)}'|sed 's/"//g')
date_de_second=$(date -d "$dat_de" +%s)
if [[ $date_de_second -lt $lyear_old_sec ]]
then
echo $i
fi
done