AWK 다중 문자 구분 기호

AWK 다중 문자 구분 기호

Ubuntu에서 Bash를 사용하고 있으며 질문은 다음과 같습니다.

헤더와 구분 기호가 포함된 큰 텍스트 파일이 있습니다 #|#.

이 파일에 대한 정보를 얻기 위해 AWK를 사용하려고 합니다. 이제 다음 표현식을 사용하여 열 1 값으로 그룹화된 열 2의 합계를 계산하려고 합니다.

awk 'BEGIN { FS="\\#\\|\\#" }{arr[$1]+=$2} END {for (i in arr) {print i,arr[i]}}' myfile.txt

내가 얻은 출력에는 두 가지 문제가 있습니다.

  • 첫째, 열 1이 두 개의 고유 값 value1과 value2를 사용한다고 가정하면 AWK는 2개가 아닌 3개의 그룹(value1, value2 및 name_column1)을 형성합니다.

    파일의 첫 번째 줄이 헤더라는 것을 이해하지 못하는 것 같습니다 ...

  • 두 번째 문제는 내 출력이 다음과 같다는 것입니다.

    value1        0
    value2        0
    name_column1  0
    

    따라서 우리는 출력의 마지막 줄이 예상치 못한 것이라는 것을 알고 있습니다(앞서 언급했듯이). 처음 두 줄에 집중하겠습니다. 여기서는 두 합계가 모두 비어 있지만 그 중 적어도 하나는 엄격하게 0보다 커야 한다는 것을 알고 있습니다.

    awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=2;}END{print sum1;}' myfile.txt
    

    나에게주세요 251597850.

따라서 마지막 명령(일반 합계)에 문제가 있거나 이전 명령(합계 + 그룹 기준)에 문제가 있습니다.

이 문제를 해결하는 방법을 아는 사람이 있나요?

편집: 내 파일 텍스트는 다음과 같습니다.

Column1#|#Column2#|#Column3

0300#|#0.00#|#0000

여기서 0300은 value1앞서 언급한 것입니다(숫자가 아니라 카테고리임).

편집 2:

awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=2;}END{print sum1;}' myfile.txt

나에게 2*(파일의 줄 수)를 제공하는데 이는 분명히 내가 원하는 것이 아니므로 명령은 다음과 같아야 합니다.

awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=$2;}END{print sum1;}' myfile.txt

편집 3:

구분 기호로 인해 내 명령이 모두 잘못된 것으로 나타났습니다. 따라서 그룹화하는 올바른 명령은 다음과 같습니다.

awk 'BEGIN { FS="#[|]#" } FNR>1 {arr[$1]+=$2} END { for (i in arr) print i,arr[i] }' file.txt

답변1

간단히 대답하자면 이 경우 FS 변수는 RE(정규식 또는 패턴)입니다. 따라서 실제 데이터 문자가 RE 컨텍스트에서 "특수"인 경우 RE에서 이스케이프하여 연산자가 아닌 자체로 처리되도록 해야 합니다.

이 경우 범인은 |교대 연산자입니다. 양쪽에 있는 항목은 대체 RE이며, 그 중 어느 하나라도 일치하는 것으로 간주됩니다. 예를 들어, 필드 구분 기호는 a|u|o|i|e각 모음에서 필드를 분할합니다.

따라서 RE는 #|#다소 중복됩니다. 필드 구분 기호로 두 번 지정되고 #반복이 무시됩니다.

해결책은 이스케이프하는 것입니다 |. 제가 선호하는 방법은 그 자체를 나타내기 위해 다운그레이드되는 |대괄호 표현식(문자 클래스)으로 변환하는 것입니다 .[|]|

또는 이스케이프 문자를 전달하여 구분 기호 \#\\|#.

탈출은 \왜 두 번이나 썼나요? 이것은 또 다른 이상한 규칙입니다(백슬래시가 종종 awk 모드에서 문제를 일으키는 이유이기도 합니다).

awk RE를 작성하는 방법에는 와 같은 패턴으로 작성 /myRE/하거나 와 같은 문자열로 작성 하는 두 가지 방법이 있습니다 "myRE".

/myRE/형식은 (기본적으로) 부울로 작동하며 pattern { action }awk 소스 모델이나 { if (/myRE/) ...}. 또한 필드나 변수와 같은 보다 구체적인 대상과 $6 ~ /myRE/일치시킬 수도 있습니다 myVar ~ /myRE/. 이 형식에서는 문자가 개별적으로 이스케이프됩니다 \.

그러나 RE가 문자열로 작성되면 awk는 나중에 RE로 호출될 수 있다는 사실을 모릅니다. 파싱됨두 배: 원래 소스 코드에서 먼저 일반적인 문자열 이스케이프(예: \t탭, \n줄 바꿈 및 \\백슬래시) 를 수행합니다. 그런 다음 ~연산자 또는 match()or 함수와 함께 split()사용될 때 다시 수행합니다 .

FS 문은 문자열로 처리되므로 모든 백슬래시를 두 배로 늘려야 합니다. 이는 명령줄에서 FS를 사용하거나 선언하든지 -F, 또는 그와 같이 FS를 선언하든 마찬가지입니다.-v FS=BEGIN { FS = "myRE" }

나는 "짧은 대답"을 언급했는데, 이와 같은 것은 거의 항상 잘못된 것입니다. 예외가 있고, 그 예외에도 예외가 있습니다.

특수 연산자에는 작동할 항목이 필요하기 때문에 단일 문자 정규식을 작성하는 것은 어렵습니다. 따라서 FS의 모든 단일 문자 값은 문자 그대로 처리됩니다. '-F|'또는 필드를 파이프 기호로 구분하여 작성할 수 있습니다 -v 'FS=|'.BEGIN { FS = "|" }

단일 문자 규칙의 예외는 단일 공백으로 구성된 FS입니다(기본값). 이것은 줄의 각 단어를 필드로 변환합니다. awk와 마찬가지로 단순은 비교 용어입니다.

(1) 구분 기호는 ASCII 공백, 가로 탭 및 개행 문자가 연속적으로 혼합된 시퀀스로 정의되는 "공백"입니다. (대체 레코드 구분 기호가 유효한 경우에만 개행 문자가 표시됩니다.)

(2) 행 전체의 선행 및 후행 공백은 필드 구분자가 아닙니다. (다른 FS가 줄의 시작이나 끝에 있으면 각각 그 앞이나 뒤에 암시적인 추가 공백 필드가 있습니다.)

제가 참조할 곳은GNU/awk 온라인 매뉴얼.

답변 자체는 엄청나게 길고 복잡하지만 매뉴얼에서는 섹션 3, 정규 표현식에 약 600줄을 할당하고 섹션 4.5, 필드 구분 방법 지정에 또 다른 250줄을 할당합니다.

관련 정보