파일 때문에 고민 중이에요. 일반적으로 호스트당 한 줄을 정의해야 합니다. 그러나 때때로 누군가가 일부 필드를 다른 행으로 분할하는 경우가 있습니다. 예는 다음과 같습니다.
"host1","host1","linux
server",""
"host2","host2","linux server",""
이제 나는 이 문제를 해결할 수 있는 방법(bash에서 더 나은 방법)을 찾고 싶습니다.
"host1","host1","linux server",""
"host2","host2","linux server",""
각 필드는 큰따옴표로 묶어야 합니다. 그렇지 않은 경우 \n
a가 삽입된 다음 이를 제거하여 행당 항상 4개의 필드가 있음을 의미합니다.
설명을 여러 줄로 나눌 수 있습니다. 예를 들면 다음과 같습니다.
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
awk
나는 다음과 같은 여러 가지 접근법을 시도했습니다 .
awk 'BEGIN {ORS=""; RS="\"\n\""; FS="\",\""; OFS="\",\""} {if (NF == 3) print "\"" $1 "\"," $2 "\"," $3 "\"\n"; else printf "%s", $0} END {print ""}' /tmp/ngr4
그러나 나는 성공하지 못했고 이 강력한 도구로 인해 한계에 도달하기 시작했습니다.
답변1
사용밀러( mlr
), 다양한 구조화된 문서 형식에 대한 CSV 지원 기능을 갖춘 다목적 처리 유틸리티로 모든 필드에서 공백을 정리하는 데 사용됩니다.
$ cat file
"host1","host1","linux
server",""
"host2","host2","linux server",""
$ mlr --csv -N clean-whitespace file
host1,host1,linux server,
host2,host2,linux server,
데이터를 file
헤더 없는 CSV 레코드로 읽고 적용합니다.clean-whitespace
작업모든. 이 clean-whitespace
작업은 각 필드 값에서 측면 공백을 잘라내고 연속 공백 문자를 단일 공백으로 결합합니다.
로 변경줄 바꿈을 공백으로만 바꾸세요, 짧은 문을 사용하여 필드를 반복할 수 있습니다.put
표현하다:
$ mlr --csv -N put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
host1,host1,linux server,
host2,host2,linux server,
gssub()
기능이는 Awk처럼 동작 gsub()
하지만 쿼리 매개변수를 정규식으로 처리하지 않습니다(Miller도 마찬가지입니다 gsub()
).
꼭 필요한 것은 아니더라도 필드에 인용을 붙여야 한다고 생각되면(필드 값에 필요한 경우 Miller가 자동으로 인용을 추가합니다) 옵션 mlr
과 함께 사용하세요 --quote-all
.
$ mlr --csv -N --quote-all clean-whitespace file
"host1","host1","linux server",""
"host2","host2","linux server",""
$ mlr --csv -N --quote-all put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
"host1","host1","linux server",""
"host2","host2","linux server",""
답변2
마지막으로 하고 싶은 일은 bash에서 이 작업을 수행하는 것입니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?.
이제 원하는 것을 "문자 바로 뒤에 나오지 않는 한 줄 바꿈을 제거하십시오 "
"라고 표현할 수 있다면 다음을 수행할 수 있습니다.
perl -pe 's/(?<!")\n/ /g' file
(?<!")\n
앞에 없는 개행 문자와 일치합니다 "
. 따라서 다음과 같은 입력 예가 주어집니다.
$ cat file
"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
위 명령은 다음을 제공합니다.
$ perl -pe 's/(?<!")\n/ /g' file
"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""
그런데 사실 mlr
이게 가장 좋은 방법이에요.
답변3
사용행복하다(이전 Perl_6)
@terdon의 뛰어난 Perl 답변에서 영감을 얻었습니다.
~$ raku -ne '/ <!after \" > $/ ?? print "$_ " !! put $_;' file
아래는 Raku(일명 Perl6)로 작성된 답변입니다. Raku에는 잘 알려진 일부 관용어를 정리하려고 시도하는 새로운 유니코드 인식 정규식 엔진이 있습니다. 따라서 (예를 들어) "Y not after X" 부정적인 되돌아보기 관용어는 <!after X > Y
Raku에서 <?after … >
긍정적인 되돌아보기를 의미하고 <!after … >
부정적인 되돌아보기를 의미합니다.
\n
Raku는 줄 종결자 처리를 표준화하므로(이제 줄 바꿈은 기본적으로 자동으로 잘립니다) Raku의 삼항 연산자를 사용하여 패턴을 감지할 수 있습니다.시험 ??
진짜 !!
잘못된, 그런 다음 print
( \n
개행을 추가하지 않고) 또는 put
( \n
텍스트 끝에 개행을 추가)을 사용하여 출력합니다.
입력 예:
"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
예제 출력:
"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""
기타 Raku 솔루션
Raku Text::CSV
모듈 사용:
Raku 생태계의 적절한 CSV 파서(모듈)를 사용하는 것은 매우 쉽습니다(참조:https://raku.land/?q=CSV). 이는 RFC 4180 준수 여부를 확인하고 표준화된 CSV 출력은 물론 광범위한 사용자 정의를 제공할 수 있습니다.
아래에서 Raku의 Text::CSV
모듈은 OP의 입력을 훌륭하게 구문 분석하고 \n
개행을 제거한 후 기본적으로 내부 공백이 있는 열만 큰따옴표로 묶인 열(첫 번째 답변)로 출력합니다. 두 번째 답변은 한 줄씩 읽고 첫 번째 답변과 동일한 답변을 생성합니다.
전체 파일을 메모리로 읽어옵니다 csv()
. 기본 출력은 다음과 같습니다.
~$ raku -MText::CSV -e 'my @a = csv(in => "/path/to/file", sep => ",");
@a = @a>>.map( *.trans: "\n" => " ");
csv(in => @a, out => $*OUT, sep => ",");'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",
한 줄씩 읽은 다음 출력을 "수동으로" 인용합니다.
~$ raku -MText::CSV -e 'my $fh = "/path/to/file"; my $io = open $fh, :r, :!chomp;
my $csv = Text::CSV.new; my @data;
while $csv.getline($io) -> $row {
@data.push: $row.map: *.trans: "\n" => " "; };
put $_.join(",") for @data>>.map({ / \s / ?? (q["] ~ $_ ~ q["]) !! $_ });'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",
https://docs.raku.org/언어/operators#infix_??_!!
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md#embedded-newlines
https://raku.org
답변4
원치 않는 개행 문자가 많더라도 큰따옴표는 모두 일치하고 필드 구분 기호는 모두 존재한다고 가정합니다. 이 경우 다음 명령을 사용할 수 있습니다.
$ sed '/^"/! s/^/ /' infile | tr -d '\n' | sed '-e s/"/"\n/'{8..1000..8}
"host1","host1","linux server centos",""
"host2","host2","linux server",""
어디:
$ cat infile
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""
이는 원치 않는 개행이 발생할 수 있는 모든 곳에서 작동합니다.
중괄호 안의 숫자 1000은 임의의 큰 숫자이며 입력 파일의 총 문자 수보다 커야 합니다.
입력 줄에 선행/후행 공백이 포함되어 있다고 의심되면 먼저 공백을 제거하십시오. 예를 들어 다음 명령을 사용하십시오.awk 'NF{$1=$1}1' infile