입력 파일입니다.
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 104914600
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991119
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 115844000
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991555
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
다음 출력이 필요합니다
QSUM HEADER STOCK DATE TIME TI PNC JPS TNN LTNN PP JP AA NS FLAG OPCODE TYPE AI TI CPO
206 CC-USER REJECT SENT TNPPP 200322 104914600 8 6 0 1 4 899599991119 12 18
206 CC-USER REJECT SENT TNPPP 200322 115844000 8 6 0 1 4 899599991555 12 18
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100 2 1499 177 123 1 4 999999999999 ORIGIN
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200 2 1499 177 123 1 4 999999999999 ORIGIN
나는 다음 샘플 awk 라이너를 시도했습니다
awk 'BEGIN{print ("QSUM HEADER STOCK DATE TIME TI PNC JPS TNN LTNN PP JP AA NS FLAG OPCODE TYPE AI TI CPO")}
/^[0-9]/{print a["o"] " " a["p"] " " a["q"]
;delete a}
/^[0-9]/{a["o"]=$0
next}
/TNN/{getline
a["p"]=$0
next}
/NS/{getline
a["q"]=$0
next}
END
{print a["o"] " " a["p"] " " a["q"];}'
각 헤더 유형에 대해 패턴을 일치시킨 후 다음 줄을 배열해야 합니다. 이 결과를 달성하기 위한 다른 좋은 대안. 이 코드는 제게는 잘 맞지만 여기서 문제는 헤더만큼 코드가 점점 길어진다는 것입니다.
답변1
FPAT
and 에는 GNU awk를 사용합니다 (그런 다음 FIELDWIDTHS
gawks의 \s/\S
약어를 사용합니다 [[:space:]]/[^[:space:]]
):
$ cat tst.awk
BEGIN { OFS="\t" }
/^\s*$/ { next } # skip blank lines
/^\s*QSUM\s/ { # start of a new record
numRecs++
lineNr = 0
}
{
if ( (++lineNr) % 2 == 1 ) {
# tags line so find every tag and field width
FPAT = "\\S+\\s*"
$0 = $0
for (i=1; i<=NF; i++) {
tag = $i
fw = (i>1 ? fw " " : "") (i<NF ? length(tag) : "*")
gsub(/^\s+|\s+$/,"",tag)
if ( !(tag in tagNames2nrs) ) {
tagNrs2names[++numTags] = tag
tagNames2nrs[tag] = numTags
}
fldNr2tagNr[i] = tagNames2nrs[tag]
}
FPAT = ""
}
else {
# vals line so use the field widths found for tags
FIELDWIDTHS = fw
$0 = $0
for (i=1; i<=NF; i++) {
val = $i
gsub(/^\s+|\s+$/,"",val)
tagNr = fldNr2tagNr[i]
vals[numRecs,tagNr] = val
}
FIELDWIDTHS = ""
}
}
END {
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = tagNrs2names[tagNr]
printf "%s%s", tag, (tagNr<numTags ? OFS : ORS)
}
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = vals[recNr,tagNr]
printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
}
}
}
.
$ awk -f tst.awk file | column -s$'\t' -t
QSUM HEADER STOCK DATE TIME TI JPS TNN LTNN PP JP AA NS CPOODE TYPE AI PNC FLAG CPO
206 CC-USER REJECT SENT TNPPP 200322 104914600 8 6 0 1 4 899599991119 12 18
206 CC-USER REJECT SENT TNPPP 200322 115844000 8 6 0 1 4 899599991555 12 18
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100 2 177 123 1 4 999999999999 1499 ORIGIN
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200 2 177 123 1 4 999999999999 1499 ORIGIN
각 행 쌍에는 서로 다른 필드가 있고 그 중 일부는 비어 있으므로 데이터를 읽으려면 고정 필드에 의존해야 하지만, 읽으려고 시도하기 전에는 필드 수와 필드 너비를 알 수 없습니다. 따라서 이 스크립트는 FPAT를 사용하여 각 태그 줄에서 모든 태그(이름, 제목, 제목)를 찾습니다.
TI JPS TNN LTNN PP JP
각 필드의 너비(태그 이름 및 다음 공백 포함)를 결정한 다음 FIELDWIDTHS를 사용하여 후속 값 행에서 값을 읽을 수 있습니다.
8 6 0 1
고정 너비 필드이므로 필드가 비어 있는 경우에도 마찬가지입니다.
나는 그것에 대해 언급하거나 일반적으로 설명하지 않을 것입니다. IMHO 코드는 매우 명확하고 텍스트 조작에 대한 많은 질문을했기 때문에 이제 일부 코드를 읽고 awk에 대해 배울 시간입니다. 매뉴얼 페이지를 찾아보십시오 print
. 변수가 어떤 값을 가지고 있는지 확인해야 하는 곳에 명령문 등을 추가해야 합니다. 구체적인 질문이 있으시면 언제든지 문의해 주시기 바랍니다.
위의 내용은 귀하의 질문과 동일한 파일인 이 입력 파일에 대해 실행되었지만 QSUM, STOCK, DATA 및 TIME 헤더의 일관되지 않은 정렬이 수정되었습니다. 귀하의 실제 입력이 그렇게 엉망일 것이라고 생각하지 않기 때문입니다. up (그러나 그렇다면 처리하기 쉬울 것입니다):
$ cat file
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 104914600
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991119
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
206 CC-USER REJECT SENT TNPPP 200322 115844000
TI JPS TNN LTNN PP JP
8 6 0 1
AA NS CPOODE
4 899599991555
TYPE AI
12 18
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124100
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
QSUM HEADER STOCK DATE TIME
103 SUITE FAIL, SUBTRACT FAILURE TPNRM 200318 031124200
TI PNC TNN PP JP AA
2 1499 177 123 1 4
NS FLAG
999999999999 ORIGIN
TI CPO
답변2
저는 이 문제를 한번에 해결하고 싶습니다. BEGIN 섹션부터 시작하여 원하는 배열 열을 포함하는 배열을 만듭니다.산출, 을 위한. 열 너비 및 텍스트(첫 번째):
BEGIN {
defCol[ 1] = " 5 QSUM";
defCol[ 2] = " 28 HEADER";
...
defCol[19] = " 6 CPO";
}
제목만 인쇄하는 함수를 작성하여 이를 확인하세요. 다음과 같이 defCol을 반복할 수 있습니다.
printf ("%.*s", 0 + defCol[j], substr (defCol[j], 5);
그런 다음 빈 줄을 생략하고 하나의 QSUM 입력과 다음 QSUM 입력 사이의 입력 줄 집합을 수집하는 함수를 작성합니다. 대문자와 공백만 포함된 행은 머리글이고, 다른 콘텐츠가 포함된 행은 데이터이며, 필드는 이전 머리글 행에 맞춰 정렬됩니다. (입력 및 출력의 맨 위 행이 잘못 정렬된 것이 오타라고 가정합니다.)
각 그룹에 대해 헤더를 열 이름과 일치시켜 데이터 항목이 속한 비둘기집을 결정합니다(당연히 defCol과 동일한 배열에 대한 인덱스로). 그런 다음 defCol 인덱스를 사용하여 필드를 정렬하고 printf 포맷터의 defCol 너비를 사용하여 세부 행을 인쇄할 수 있습니다.
복잡해 보이지만 유연하고(가까운 미래에 다른 유사한 문제에 직면하게 될 것 같습니다) 체계적입니다. 이전에 본 적이 없는 헤더, 보고서에 너무 긴 필드 등을 표시할 수도 있습니다.
더 자세한 기능 설명:
먼저, 입력 그룹이 완료되는 시기를 감지하는 조건이 필요합니다. 이는 "제어 중단"이었습니다. 이는 "QSUM HEADER" 또는 유사한 내용이 포함된 줄에서 발생하며 일부 패턴을 사용하여 다른 간격을 허용합니다. 또한 (아직 그룹이 없기 때문에) 이 작업이 처음으로 발생하는 것을 원하지 않으며 END 조건에서도 발생해야 합니다(마지막 그룹에는 이를 트리거할 제목이 없기 때문에). ).
입력의 나머지 부분을 사용하여 모든 헤더 부분을 하나의 긴 문자열에 추가하고 모든 데이터 부분을 다른 긴 문자열에 추가하고 모든 빈 줄을 무시할 수 있습니다.
그룹을 출력하려면 헤더 문자열을 통해 데이터 헤더의 위치를 찾고 match() 및 substr() 함수를 사용하여 데이터 문자열에서 해당 위치의 필드를 선택하면 됩니다. 헤더 항목과 동일한 인덱스를 사용하여 각 데이터 항목을 배열에 저장할 수 있습니다.
그런 다음 제목 고정 텍스트를 출력하는 것처럼 데이터 필드를 출력할 수 있습니다.
이 모든 것이 혼란스럽게 들리지만 실제로는 매우 간단합니다. 지금은 코딩할 수 없지만 오늘 나중에 프레임워크를 게시할 수 있을 것입니다.
실제로 입력(게시됨)은 간격의 변화로 인해 안정적으로 구문 분석되지 않을 수 있습니다.
예를 들어 STOCK, DATE, TIME 아래의 값이 일치하지 않습니다. HEADER에는 공백이 있으므로 필드 수를 셀 수 없습니다. 두 개 이상의 공백을 구분 기호로 사용하거나 "충분히 가까운" 필드 정렬을 사용합니다. 이것은 단지 40줄의 예일 뿐입니다. 다른 위험도 있습니다.