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