"awk"를 사용하여 여러 데이터 청크로 자리 표시자를 인쇄합니다.

"awk"를 사용하여 여러 데이터 청크로 자리 표시자를 인쇄합니다.

좋은 아침이에요,

사용자 입력을 기반으로 1~8개의 변수(아래에서 "CONDx"로 표시됨)를 포함하는 여러 데이터 블록이 있습니다. 열 형식으로 표시할 데이터를 추출하기 위해 awk와 grep을 사용하여 스크립트를 작성했습니다. 더 큰 파일에서 데이터를 추출했으므로 솔루션에서 한 걸음 물러나야 할 수도 있습니다. 어쨌든 데이터는 다음과 같습니다.

> cat file
foo
REF    Data1
COND1  Value1
COND2  Value2
foo
REF    Data2
COND3  Value3
foo
REF    Data3
COND1  Value4
COND3  Value5
foo

내 스크립트는 다음 형식으로 결과를 표시합니다. 올바르게 정렬하려면 수동으로 수직으로 수정해야 합니다.

        COND1   COND2   COND3   COND4   COND5   COND6   COND7   COND8
Data1   Value1  Value2  Value3  x       x       x       x       x               
Data2   Value4          Value5  
Data3

제 질문은 awk(또는 sed 등)를 사용하여 각 CONDx가 각 REF 블록에 포함되어 있는지 확인하고 해당 "ValueX"를 인쇄하는 경우, 그렇지 않은 경우 "x"(또는 Better)를 인쇄하는 것이 가능합니까? 공백)을 자리 표시자로 사용합니까? 따라서 원하는 출력은 다음과 같습니다.

        COND1   COND2   COND3   COND4   COND5   COND6   COND7   COND8
Data1   Value1  Value2  x       x       x       x       x       x       
Data2   x       x       Value3  x       x       x       x       x
Data3   Value3  x       Value5  x       x       x       x       x

COND1을 예로 들면 스크립트의 일부에는 다음이 포함됩니다.

 grep COND1 file | awk '{print $2} END { if (!NR) print "x" }' > temp.cond1

temp.cond1을 결과 파일에 붙여넣었지만 출력에 표시된 것처럼 첫 번째 줄에만 "x"가 인쇄됩니다. 왜 작동하지 않는지 이해하지만 이를 수행하는 새로운 방법을 생각할 수 없습니다. IF 문으로 할 수 있을 거라 생각했나요? 어떤 도움이라도 대단히 감사하겠습니다.

시간 내 주셔서 감사합니다.

답변1

이것은 awk에서의 구현입니다. 언어로 몇 줄 이상의 프로그램을 작성하고 그것이 재미있는 연습이 될 것이라고 생각한 지 꽤 오래되었습니다.

프로그램을 사용하여 awk를 실행하려면 플래그를 지정해야 합니다 -f. 예를 들면 다음과 같습니다.

awk -f my_program.awk my_data.txt

이 구현은 파일에 있는 CONDx 변수만 출력합니다.

# Initialize a couple of variables
BEGIN {
    fill_value = "xx"
    record_number = 0
}

# for any line that begins and ends with `foo` save the record
# and then move on to process the next line
/^foo$/ { save_record(); next }

# for any other line, grab the key and data, and mark that the record is valid
{
    fields[$1] = $1
    record[$1] = $2;
    record[1] = "exists"
}

# after reading in all of the records, output them
END {
    # sort the fields into alpha order
    asort(fields)
    delete fields["REF"]

    printf("%-8s", "REF")
    for (field in fields) {
        printf("%-8s", fields[field])
    }
    print ""

    # print the records
    for (i=0; i < record_number; i++) {
        record_name = record_number_str(i, "REF");
        printf("%-8s", records[record_name])

        for (field in fields) {
            record_name = record_number_str(i, fields[field])
            to_print = fill_value
            if (record_name in records)
                to_print = records[record_name]
            printf("%-8s", to_print)
        }
        print ""
    }
}

function save_record() {
    if (1 in record) {
        delete record[1]
        for (rec in record)
            records[record_number_str(record_number, rec)] = record[rec]
        record_number++
    }
    delete record
}

# awk only has single dimensional associative arrays.  So we need
# to construct a key for the array that has two dimensions
function record_number_str(record_number, rec) {
    return sprintf("%06d %s", record_number, rec)
}

나는 awk가 가장 이상적인 언어가 아니라고 생각합니다. 더 나은 방법은 Perl, Ruby 또는 Python입니다. 비교를 위해 다음은 Python 구현입니다. 행 수는 약 1/2에 불과합니다.

import fileinput

record = {}
records = []
fields = set()
for line in [l.strip() for l in fileinput.input()]:
    if line == 'foo':
        if record:
            records.append(record)
            record = {}
    else:
        key, value = line.split()
        record[key] = value
        fields.add(key)

# print the header
print("%-8s" % "REF", end="")
fields.remove("REF")
for field in sorted(fields):
    print("%-8s" % field, end="")
print()

# print the records
for record in records:
    print("%-8s" % record["REF"], end="")
    for field in sorted(fields):
            print("%-8s" % record.get(field, ''), end="")
    print()

관련 정보