디렉터리에 있는 4개의 CSV 파일 각각에 대한 날짜 범위를 찾고 싶습니다. 날짜 열은 각 csv의 마지막 열이며 형식은 입니다 1/25/2012 7:20:55 PM
.
날짜 필드는 항상 마지막 열인 24열입니다. 각 CSV의 레코드만 변경됩니다. 그리고 날짜가 정해졌습니다.
각 파일에 대해 이 작업을 수행하고 최종 범위를 얻을 수 있는 방법이 있습니까(날짜가 정렬되어 있다고 가정)?
따라서 첫 번째 기록이 2012년 1월 25일 오후 7시 20분 55초라면 마지막 기록은 2016년 11월 7일 오후 2시 36분 20초입니다.
출력 날짜 범위를 2012년 1월 25일부터 2016년 11월 7일까지로 설정하고 싶습니다. 하지만 4개 파일의 날짜 범위를 모두 병합하고 싶습니다.
입력 예(간결함을 위해 일부 열은 생략됨):
첫 번째 파일:
열 1,2 열,3 열,...,칼럼 23,col24_time 값 1,값 2,값 3,...,값 23,2012년 1월 25일 오후 7시 20분 값 1,값 2,값 3,...,값 23,2012/1/26 오전 10:57 값 1,값 2,값 3,...,값 23,2012년 1월 26일 오후 2시 20분 값 1,값 2,값 3,...,값 23,2012년 1월 30일 오전 11시 55분 값 1,값 2,값 3,...,값 23, 2012년 1월 30일 오후 3시 17분 값 1,값 2,값 3,...,값 23,2012년 1월 30일 오후 5시 36분 값 1,값 2,값 3,...,값 23,2012년 1월 30일 오후 8시 16분 ... 값 1,값 2,값 3,...,값 23,2012년 4월 11일 오전 11시 45분 값 1,값 2,값 3,...,값 23,2012년 4월 11일 오후 2시 23분
중간 파일
마지막 파일:
값 1,값 2,값 3,...,값 23,2015년 3월 11일 오전 4시 45분 값 1,값 2,값 3,...,값 23,2015년 3월 11일 오전 8시 40분 ... 값 1,값 2,값 3,...,값 23,2016년 11월 7일 오후 2시 36분
각 파일에는 거의 5-10K 레코드가 있습니다. 날짜는 파일에서 순차적으로 정렬됩니다. 각 파일의 각 열에는 헤더가 있습니다.
이 명령의 출력은 다음 head -n7 Files/file1.csv | cut -d, -f24
과 같습니다.
"col24_time"
"2012-01-01 00:30:26"
"0"
"2012-01-01 02:00:37"
"0"
"0"
"https://external.xx.fbcdn.net/safe_image.php?" <<-- previous column record?
답변1
나는 아직도 그 질문을 이해했는지 잘 모르겠습니다. 그러나 지정된 입력을 기반으로 원하는 출력을 생성하는 코드는 다음과 같으며 다른 답변보다 훨씬 짧습니다.
datetime1=$(head -n1 file1.csv | cut -d, -f24)
datetime4=$(tail -n1 file4.csv | cut -d, -f24)
printf '%s - %s\n' "${datetime1%% *}" "${datetime4%% *}"
그러면 첫 번째 파일에서 첫 번째 행을 가져오고 네 번째 및 마지막 파일에서 마지막 행을 가져오고 24번째 필드를 추출합니다(기준:,
각각에 대한 구분 기호). 구체적으로 1/25/2012 7:20 AM
는 날짜/시간 문자열입니다 11/7/2016 2:36 PM
. 그런 다음 첫 번째 공백과 그 뒤의 모든 내용을 제거하여 각 단어의 첫 번째 "단어"를 인쇄합니다. 필수 날짜입니다.
이는 준한 줄로 된 것과 같습니다. 가독성을 위해 세 줄로 나누었지만 논리적으로는 긴 명령입니다.
printf '%s - %s\n' \
"$(head -n1 file1.csv | cut -d, -f24 | cut -d' ' -f1)" \
"$(tail -n1 file4.csv | cut -d, -f24 | cut -d' ' -f1)"
여기서는 변수를 사용하지 않기 때문에 매개변수 확장을 사용할 수 없기 때문에 두 번째 를 사용하여 24번째 필드의 첫 번째 "단어"를 추출했습니다 cut
.
답변2
데이터가 이미 날짜별로 정렬되어 있고 일관된 구조를 갖고 있는 경우 이를 사용 sed
하여 특정 행을 처리할 수 있습니다.
sed -E -n "2 {s/.*,([^ ]*).*/\1 - /;h}; $ {s/.*,([^ ]*).*/\1/;H;x;s/\n//;p}" file
첫 번째 파일의 출력은 다음과 같습니다.
1/26/2012 - 4/11/2012
모든 파일을 함께 넣습니다 cat
(날짜 순서로 이름이 지정되고 올바른 순서로 파이프된다고 가정).
cat file* | sed ...
1/26/2012 - 11/7/2016
송곳
기본값을 인쇄하지 않음 sed
으로 설정-n
sed -E -n "
두 번째 줄을 잡고 캡처 그룹으로 2
원하는 줄 부분을 수집 ([^ ]+)
하고 출력을 패턴 공간의 캡처 및 구분 기호로 결합합니다.\1 -
2 {s/.*,([^ ]+).*/\1 - /;
h
이전 공간 으로 밀어 넣습니다 ( h
이전에 존재했던 모든 내용을 지움).
h};
위 $
도선에서 패턴공간에서 다시 원하는 선 부분을 잡아주세요
$ {s/.*,([^ ]+).*/\1/;
기존 공간에 새 패턴 공간을 추가하고 기존 콘텐츠와 새 콘텐츠 사이에 H
ewline을 추가 (ewline 추가)한 다음 패턴 공간을 사용하여 예약된 공간의 전체 콘텐츠를 변경합니다.\n
H
\n
x
H;x;
이제 결합된 출력이 패턴 공간에 있습니다. 원하지 않는 줄 \n
바꿈과 p
린트 만 제거하면 됩니다.
s/\n//;p}" file
답변3
Unix 파이프를 선호하는 경우 다음을 수행할 수 있습니다.
# standalone example: this converts from a 2-colum, 1-line "csv" to unixtime,
# and converts back to readable date
echo "2,1/25/2012 7:20:55 PM" \
| perl -aF, -MDate::Parse -E "say Date::Parse::str2time(\$F[1])" - \
| xargs -i date "+%D " -d@{}
# result
01/25/12
이는 오래되었지만 핵심이 아닌 Perl 모듈에 의존하며 Date::Parse
, 아직 없는 경우 먼저 설치해야 합니다. 구성 클라이언트 설치를
사용하거나 0으로 설정할 수도 있습니다 .cpan Date::Parse
cpanm Date::Parse
따라서 귀하의 예에서는 두 개의 단일 행에서 가장 어린 데이터와 가장 오래된 데이터를 얻으려고 할 수 있습니다.
perl -aF, -MDate::Parse -E "say Date::Parse::str2time(\$F[5])" *.csv \
| sort \
| sed -e 1b -e '$!d' \
| xargs -i date "+%D " -d@{}
# result
01/25/12
11/07/16
라인 sed
은이 게시물이 웹사이트에서.
답변4
다음 awk
프로그램이 실행됩니다(이라고 부르겠습니다 timerange.awk
). 특정 순서로 파일을 제공할 필요 없이 간단히 사용할 수 있도록 설계되었습니다 *.csv
. 그렇지 않으면 타임스탬프가 순서대로 지정되어 있으므로 디렉터리의 첫 번째 파일과 마지막 파일만 제공하면 됩니다.
#!/usr/bin/awk -f
# For every line of the files (after the first, which contains headers)
FNR>1{
# Break the time stamp field into its individual components and reassemble
# in a way that 'mktime' understands, to generate an epoch-based timestamp
# for "later/earlier than"-type comparisons.
split($NF,a,/[ /:]/);
if (a[6]=="AM" && a[4]==12) a[4]=0;
if (a[6]=="PM") a[4]=a[4]+12;
tst=a[3]" " a[1] " " a[2] " " a[4] " " a[5] " 00";
curr_ts=mktime(tst);
# If we are on the first "data" row of the first file, initialize start and end
# date
if (NR==2)
{
end=start=$NF;
end_ts=start_ts=curr_ts;
}
# On all later lines, check if the timestamps associated with the "start"
# and "end" time specifications are later resp. earlier than that of the
# current line. If so, update "start" and "end" specifications.
else
{
if (curr_ts>end_ts) {end_ts=curr_ts; end=$NF};
if (curr_ts<start_ts) {start_ts=curr_ts; start=$NF};
}
}
# After the last file was processed: Output the human-readable range
END{print start " - " end}
당신은 그것을 호출할 수 있습니다
awk -F, -f timerange.awk file1.csv file2.csv ...
아니면 단순히
awk -F, -f timerange.awk *.csv
파일의 순서는 중요하지 않기 때문에 전역 "첫 번째" 및 "마지막" 항목을 자동으로 찾습니다.
예제 입력의 출력(현재 형식 - 첫 번째 설명과 반대라고 가정합니다. 타임스탬프는아니요초 포함):
1/25/2012 10:57 AM - 11/7/2016 2:36 PM
고쳐 쓰다
하루 중 시간을 완전히 무시하려면 프로그램을 중단하면 됩니다.
#!/usr/bin/awk -f
# For every line of the files (after the first, which contains headers)
FNR>1{
split($NF,a,/[ /:]/);
tst=a[3]" " a[1] " " a[2] " 00 00 00"
curr_ts=mktime(tst);
sub(/[[:space:]]+.* [AP]M$/,"",$NF);
if (NR==2)
{
end=start=$NF;
end_ts=start_ts=curr_ts;
}
else
{
if (curr_ts>end_ts) {end_ts=curr_ts; end=$NF};
if (curr_ts<start_ts) {start_ts=curr_ts; start=$NF};
}
}
END{print start " - " end}