날짜 조건에 대해 잘 모르겠어

날짜 조건에 대해 잘 모르겠어

해당 사람이 지난 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 2023OP 시스템의 "오늘"이 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"

성능 관점...

이 답변에는 단일 운영 체제 호출이 필요하며 date1개의 파일 설명자 열기/닫기가 필요합니다(출력이 다른 파일로 리디렉션되는 경우 2개).

dateGilles의 답변에는 각 입력 줄에 대한 운영 체제 호출이 필요합니다.그리고각 호출에 대해 파일 설명자를 열고 닫는 데 드는 비용이 많이 드는 오버헤드입니다 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

두 실행의 출력은 파일에 저장됩니다. diffa는 두 출력 파일에 차이가 없음을 보여줍니다(즉, 두 답변 모두 동일한 결과 세트를 생성합니다).


이 경우 OP에는 모든 필드가 큰따옴표로 묶여 있다고 명시되어 있습니다.

일부 필드를 큰따옴표로 묶을 수 없는 경우 GNU awk's 'FPAT'pair 에 대한 단일 호출만 사용하고 수행 할 수 있습니다 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 사용그리고 GNU1년 조건:

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요령 이다.awkgetline


1마지막으로 는 을 의미하므로 true특정 true조건에서는 기본적으로 print현재 행을 의미합니다.


통화에 대한 문서 close():https://www.gnu.org/software/gawk/manual/html_node/Close-Files-And-Pipes.html


epochUnix 타임스탬프: 이후 경과된 초 수입니다 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

관련 정보