열 값을 기준으로 CSV 파일에서 행 삭제

열 값을 기준으로 CSV 파일에서 행 삭제

다음 형식의 1,200만 행이 포함된 CSV 파일이 있습니다.

mcu_i,INIT,200,iFlash,  11593925,     88347,,0x00092684,r,0x4606b570,   ok,,         32,single,op-c,0,,         0,         0,         0,
mcu_i,INIT,200,iFlash,  11593931,     88348,,0x00092678,r,0x28003801,   ok,,         32,single,op-c,0,,         0,         0,         0,

다음 논리를 사용하여 여섯 번째 열의 값을 기반으로 행을 삭제하고 싶습니다. if (value >= X AND value <= Y ) => 행 삭제

gawk를 사용하여 해결책을 찾았습니다.

gawk -i inplace -F ',' -v s="$start_marker" -v e="$end_marker" '!($6 <= e && $6 >= s)' myfile.csv

하지만 시간이 너무 오래 걸리므로 더 나은 성능을 갖춘 다른 솔루션을 원합니다.

감사해요

답변1

긴 이야기 짧게

표준 출력을 gawk리디렉션 /dev/null하거나 파이핑 하면 cat속도가 크게 향상되고 실행 시간이 크게 단축됩니다.

gawk -i inplace [...] myfile.csv >/dev/null

또는:

gawk -i inplace [...] myfile.csv | cat

물에 뛰어들다

@RomeoNinov의 경우에도답변원래 명령보다 실제로 더 빠르게 실행될 것입니다. 설명하고 싶습니다.를 사용해도 더 빠릅니다 -i inplace.

당신이 보면대화형 및 비대화형 버퍼링부분적으로gawk 정보 페이지,다음 내용이 표시됩니다.

인터랙티브 프로그램은 평균 수준입니다.라인 버퍼출력합니다(즉, 각 줄을 작성합니다). 비대화형 프로그램은 버퍼가 가득 찰 때까지 기다립니다. 이는 여러 줄의 출력이 될 수 있습니다.

gawk결과가 일부 "in-place"로 인쇄될 때 표준 출력으로 인쇄되지 않는 경우에도 마찬가지인 것 같습니다.

10줄짜리 파일이 있어요.

$ cat somefile
1
2
3
4
5
6
7
8
9
10

기본적으로(파일을 변경하지 않고 모든 줄을 있는 그대로 인쇄함) 10개의 strace시스템 호출이 실행됩니다(원본 파일의 각 줄마다 하나씩).gawkwrite

$ strace -e trace=write -c gawk -i inplace 1 somefile 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000098           9        10           write
------ ----------- ----------- --------- --------- ----------------
100.00    0.000098           9        10           total

이는 대화형으로 실행되고 결과가 다음과 같기 때문입니다.라인 버퍼( gawk결과가 표준 출력이 아닌 파일에 기록되더라도 각 줄은 완료되자마자 인쇄됩니다.)

이제 stdout을 비대화형으로 만들기 위해 명령 으로 리디렉션 /dev/null(또는 파이프로 연결)하면 단일 시스템 호출만 호출되는 것처럼 보입니다 . 이는 모든 줄을 즉시 인쇄하지 않고 버퍼가 가득 찼을 때만 결과를 플러시하기 때문입니다.catstracegawkwrite

$ strace -e trace=write -c gawk -i inplace 1 somefile > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000020          20         1           write
------ ----------- ----------- --------- --------- ----------------
100.00    0.000020          20         1           total

물론 이는 누적되며 입력 파일이 클수록 대화형 실행과 비대화형 실행 간의 차이가 더 커집니다.

일반화하다

gawk대화형 모드에서는 처리가 완료되면 각 줄이 파일에 기록되므로 명령이 느립니다 . 이는 파일에 수백만 번의 쓰기를 수행함을 의미합니다.

@RomeoNinov의 솔루션은 사용하지 않기 때문에 원래 명령보다 빠르지만 inplace출력을 임시 파일로 리디렉션하므로 비대화형 모드에서 실행되어 버퍼 플러시를 최적화하고 gawk파일 작업에 대한 쓰기 작업을 더 적게 수행합니다.

그러나 질문에 제공된 명령을 계속 사용할 수 있지만 stdout을 /dev/null(어쨌든 비어 있기 때문에) 리디렉션하거나 파이프하면 cat빠르게 실행됩니다.

gawk다음과 함께 사용 시 보안 위험inplace

나는 제자리에서 작동하면 예측할 수 없는 결과를 초래할 수 있다는 @RomeoNinov의 의견에 전적으로 동의하지 않지만 @OlivierDulac의논평-i inplace이는 일반적인 사용이 보안 취약점으로 간주되는 이유를 설명하는 유용한 답변을 제공합니다 .이 문제를 해결하는 방법안전한 방법으로 실행하세요.

답변2

한 가지 가능한 방법은(명령을 다시 작성하여) 다음과 같습니다.

gawk  -F, -v s="$start_marker" -v e="$end_marker" '$6 > e || $6 < s'  myfile.csv >/tmp/newfile

현장 awk작동은 권장되지 않으며 안전상의 위험이 있습니다. 또한 스크립트가 정확하다고 100% 확신하기 전에 소스 파일을 망칠 수도 있습니다.

답변3

그렇지 않다면 awkPerl을 사용해 볼 수 있습니다.

#!/usr/bin/perl
use 5.18.2;
use warnings;
use strict;

my ($X, $Y) = (88347, 88347);
while (<>) {
    next
        if (/(?:^[^,]*,){5}\s*([^,]+)/ && $1 >= $X && $1 <= $Y);
    print;
}

정규 표현식은 한 줄에서 처음 5개의 쉼표로 구분된 필드를 건너뛰고 공백을 무시하여 6번째 필드의 나머지 부분을 캡처합니다 $1. 조건이 일치하면 해당 줄이 무시됩니다. 그렇지 않으면 이것이 출력입니다.

예를 들어 값이 88348인 행을 출력합니다.

.perl your_script input_file(s) > output_file

답변4

사용행복하다(이전 Perl_6)

~$ raku -MText::CSV -e 'my @rows; my $csv = Text::CSV.new( sep => ",");  \
                        while ($csv.getline($*IN)) -> $row { @rows.push: $row.map(*.trim) if 88000 < $row.[5] < 98000; };  \
                        .join(",").put for @rows;'  <  ~/raphui_771255.csv

Raku는 Perl 계열의 프로그래밍 언어입니다. 유니코드와 강력한 정규식 엔진에 대한 고급 지원을 제공합니다.

위의 답변은 Raku의 Text::CSV모듈을 사용합니다. Perl(5) 모듈은 Text::CSV_XS좋은 반응을 얻었으며 오랫동안 모듈 작성자/유지관리자가 Text::CSVRaku(H. Merijn Brand, 개인 커뮤니케이션)용 모듈을 개발했습니다.

입력 예(@aborruso에게 감사드립니다!):

mcu_i,INIT,200,iFlash,  11593925,     88347,,0x00092684,r,0x4606b570,   ok,,         32,single,op-c,0,,         0,         0,         0,
mcu_i,INIT,200,iFlash,  11593931,     88348,,0x00092678,r,0x28003801,   ok,,         32,single,op-c,0,,         0,         0,         0,
mcu_i,INIT,200,iFlash,  10593931,     88348,,0x00092678,r,0x28003801,   ok,,         32,single,op-c,0,,         0,         0,         0,
mcu_i,INIT,200,iFlash,  21593931,     98348,,0x00092678,r,0x28003801,   ok,,         32,single,op-c,0,,         0,         0,         0,
mcu_i,INIT,200,iFlash,  31593931,     108348,,0x00092678,r,0x28003801,   ok,,         32,single,op-c,0,,         0,         0,         0,

예제 출력:

mcu_i,INIT,200,iFlash,11593925,88347,,0x00092684,r,0x4606b570,ok,,32,single,op-c,0,,0,0,0,
mcu_i,INIT,200,iFlash,11593931,88348,,0x00092678,r,0x28003801,ok,,32,single,op-c,0,,0,0,0,
mcu_i,INIT,200,iFlash,10593931,88348,,0x00092678,r,0x28003801,ok,,32,single,op-c,0,,0,0,0,

참고: Raku는 "연결된" 불평등을 허용합니다. 또한 Raku에는 하드 코딩된 값 대신 %*ENV셸 변수에 액세스하는 데 사용할 수 있는 특수 연관 배열이 있습니다. 따라서 다음은 환경(즉, 쉘)에서 쉘 startMarker변수를 가져옵니다 stopMarker. 출력에 고급 csv( …, out => $*OUT)기능을 사용하면 공백이 포함된 문자열이 자동으로 인용됩니다(귀하의 경우 .trim호출도 제거하세요).

~$ env startMarker="88000" stopMarker="89000"     \
   raku -MText::CSV -e 'my $start = %*ENV<startMarker>; my $stop =  %*ENV<stopMarker>;     \
                        my @rows; my $csv = Text::CSV.new( sep => ",");    \
                        while ($csv.getline($*IN)) -> $row { @rows.push: $row if $start < $row.[5] < $stop; };    \
                        csv(in => @rows, out => $*OUT);'  <  ~/raphui_771255.csv
mcu_i,INIT,200,iFlash,"  11593925","     88347",,0x00092684,r,0x4606b570,"   ok",,"         32",single,op-c,0,,"         0","         0","         0",
mcu_i,INIT,200,iFlash,"  11593931","     88348",,0x00092678,r,0x28003801,"   ok",,"         32",single,op-c,0,,"         0","         0","         0",
mcu_i,INIT,200,iFlash,"  10593931","     88348",,0x00092678,r,0x28003801,"   ok",,"         32",single,op-c,0,,"         0","         0","         0",

https://raku.land/zef:Tux/Text::CSV
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md
https://docs.raku.org
https://raku.org

관련 정보