다음 형식의 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
시스템 호출이 실행됩니다(원본 파일의 각 줄마다 하나씩).gawk
write
$ 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
(또는 파이프로 연결)하면 단일 시스템 호출만 호출되는 것처럼 보입니다 . 이는 모든 줄을 즉시 인쇄하지 않고 버퍼가 가득 찼을 때만 결과를 플러시하기 때문입니다.cat
strace
gawk
write
$ 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
그렇지 않다면 awk
Perl을 사용해 볼 수 있습니다.
#!/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::CSV
Raku(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