awk 또는 sed를 통해 첫 번째 열로 행 결합

awk 또는 sed를 통해 첫 번째 열로 행 결합

awk다음과 같은 상황에서는 어떻게 사용하나요?

같은 열로 시작하는 행을 연결하고 싶습니다. 조인 후에는 첫 번째 열(이 경우 )만 aaa유지 www됩니다 hhh.

파일은 공백 또는 탭으로 구분될 수 있습니다.

입력 예:

aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL

원하는 출력:

aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL

배경은 첫 번째 열이 항상 엔터티의 식별자인 매우 간단한 파일 기반 데이터베이스를 구축하고 싶다는 것입니다. 동일한 식별자 열을 기반으로 하는 모든 행이 연결됩니다.

답변1

awk를 사용하여 각 행의 첫 번째 열을 얻으려면 다음을 수행할 수 있습니다.

< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

이는 나머지 행에 대한 키입니다. 따라서 행의 첫 번째 열을 키로 사용하고 행의 두 번째 열을 값으로 사용하여 해시 테이블을 만들 수 있습니다.

< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111

행의 나머지 부분(2열부터 시작)을 얻으려면 모든 열을 수집해야 합니다.

< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL 
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc    NULL NULL NULL NULL 
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL 

답변2

다른 사람들은 awk 또는 sed로 대답했을 수도 있지만 Python 버전은 간단하고 도움이 될 수 있습니다.

#!/usr/bin/env python

input_file = 'input.dat'
in_fh      = open(input_file, 'r')

input_order = []
seen        = {}
for line in in_fh:    
    # Remove the newline character...
    line = line[:-1]

    # Separate the first column from the rest of the line...
    key_col, sep, rest_of_line = line.partition(" ")
    rest_of_line = sep + rest_of_line  

    # If we've seen this key already, concatenate the line...
    if key_col in seen:
        seen[key_col] += rest_of_line
    # ...otherwise, record the ordering, and store the new info
    else:
        input_order.append(key_col)
        seen[key_col] = rest_of_line

in_fh.close()

# Dump the ordered output to stdout
for unique_col in input_order:
    print unique_col + seen[unique_col]

답변3

이것은 coreutils의 흥미로운 응용 프로그램입니다. 입력의 모든 행에 대해 조인을 호출하기 때문에 큰 입력에서는 그다지 효율적이지 않다고 생각됩니다.

touch outfile
while read; do
  join -a1 -a2 outfile <(echo $REPLY) > tmp
  mv tmp outfile
done < infile

효율성을 위해서는 가상 디스크 outfile에 저장하는 것이 도움이 될 수 있습니다.tmp

편집하다

또는 임시 파일이 없습니다.

out=""
while read; do
  out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile

echo "$out"

답변4

OP 가정구함입력 순서 유지원하는 출력, 연관(문자열 인덱스) 배열은 여기에서 사용할 수 없습니다. 해당 키에 대해 반복을 사용할 때 출력 순서는 for (var in array)Perl의 해시나 Python 사전처럼 예측할 수 없습니다.3.6 이전.

다음은 입력 파일에 처음 나타난 키의 원래 순서를 유지하는 잘 설명된 바닐라 AWK 솔루션입니다. 중복 제거남은칼럼은 또 다른 도전이겠지만, 꼭 그럴 필요는 없을 것 같습니다.

이는 BSD 버전의 AWK를 사용하여 macOS에서 테스트되었습니다.~해야 한다가지고 있는 모든 것을 활용해 작업하세요 awk. 원한다면 chmod +x concat-by-first-col먼저 실행 파일로 표시한 경우 독립형 쉘 스크립트*(AWK로 작성되었다는 사실을 아무도 알 수 없음)로 실행할 수 있습니다.

#!/usr/bin/awk -f
##  concat-by-first-col
##    concatenate values from lines beginning with the same first column
##
##  usage:
##    $ chmod +x concat-by-first-col
##    $ ./concat-by-first-col inputfile > outputfile

BEGIN {
    # default output separator *is* a space, but if you wanted to change it…
    OFS = " "
}

{
    # assuming EVERY input record has AT LEAST the key…
    # append to `keys` if we haven’t seen this key before
    if (!($1 in values))
        keys[length(keys)+1] = $1

    # append second and subsequent columns to what we already have
    for (i = 2; i <= NF; i++)
        # insert an OFS *only* if there’s an existing value for this key
        values[$1] = (values[$1] ? values[$1] OFS : "") $i
}

END {
    # for all the keys, in the order they appeared in the input…
    for (i = 1; i <= length(keys); i++) {
        key = keys[i]
        # a comma stands in for OFS in AWK’s `print` statement
        print key, values[key]
    }
}

독립 실행형 스크립트 대신 스위치를 사용하여 AWK에 프로그램 텍스트를 제공할 수 있습니다 -f.

awk -f concat-by-first-col inputfile > outputfile

블록 도 생략하는 경우 명령줄에서 지정할 BEGIN수 있습니다 .OFS

awk -v OFS='\t' -f concat-by-first-col inputfile > outputfile

원하는 출력과 일치합니까? 확인하다:

echo "aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL" > input

echo "aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL" > expected

# uses Bash’s process substitution**; expect no output
diff expected <(./concat-by-first-col input)

*을 위한이유, Linux를 사용 중이거나 AWK 버전이 아닌 경우 /usr/bin/awk시스템에서 "shebang" 줄을 조정해야 할 수도 있습니다.

**https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html

관련 정보