다음 파일이 있습니다.
a3 v2c
v5 a7
a9 v2c
v1c a3 a7c
원하는 출력(각 행에 중복 없음):
a3 a7c a9 v1c v2c
a7 v5
내가 원하는 것은 적어도 하나의 요소를 공유하는 행을 병합하는 것입니다. 행 2에서는 두 요소가 모두 고유하며 행은 있는 그대로(정렬된 순서로) 출력됩니다. 1행은 "v2c"를 3행과 공유하고 "a3"을 4행과 공유하므로 이 3개의 행이 결합되어 정렬됩니다. 공유 요소는 다른 열에 있을 수 있습니다.
대용량 파일(200,000줄)의 경우 코드가 매우 느립니다.
Lines=$(awk 'END {print NR}' $1)
bank=$1
while [ $Lines -ge 1 ]
do
echo "Processing line $Lines"
awk -v line=$Lines 'NR == line' $bank | awk NF=NF RS= OFS="\n" | sort | uniq > Query.$Lines
k=0
while [[ $k != 1 ]]
do
if [[ $k != "" ]]
then
grep -f Query.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query1.$Lines
grep -f Query1.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query2.$Lines
grep -f Query2.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query3.$Lines
k=$(diff Query2.$Lines Query3.$Lines)
if [[ $k != "" ]]
then mv Query3.$Lines Query.$Lines
fi
else
awk NF=NF RS= OFS=" " Query3.$Lines >> $1.output.clusters
grep -v -f Query3.$Lines $bank > NotFound.$Lines
bank=NotFound.$Lines
k=1
fi
done
rm Query*
Lines=$(( $Lines - 1 ))
done
exit
find . -maxdepth 1 -type f -size 0 -delete
rm NotFound.* Query.* Query1.* Query2.* Query3.*
저는 bash나 awk를 사용하는 더 간단하고 효율적인 솔루션이 있다고 믿습니다. 미리 감사드립니다!
답변1
배열의 배열 합계에 GNU awk 사용 sorted_in
:
$ cat tst.awk
{
for ( fldNrA=1; fldNrA<NF; fldNrA++ ) {
fldValA = $fldNrA
for ( fldNrB=fldNrA+1; fldNrB<=NF; fldNrB++ ) {
fldValB = $fldNrB
val_pairs[fldValA][fldValB]
val_pairs[fldValB][fldValA]
}
}
}
function descend(fldValA, fldValB) {
if ( !seen[fldValA]++ ) {
all_vals[fldValA]
for ( fldValB in val_pairs[fldValA] ) {
descend(fldValB)
}
}
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( fldValA in val_pairs ) {
delete all_vals
descend(fldValA)
if ( fldValA in all_vals ) {
sep = ""
for ( fldValB in all_vals ) {
printf "%s%s", sep, fldValB
sep = OFS
}
print ""
}
}
}
$ awk -f tst.awk file
a3 a7c a9 v1c v2c
a7 v5
원래 답변:
이것은시작배열의 배열에 GNU awk 사용:
$ cat tst.awk
{
for ( fldNr=1; fldNr<=NF; fldNr++ ) {
fldVal = $fldNr
fldVals_rowNrs[fldVal][NR]
rowNrs_fldVals[NR][fldVal]
}
}
END {
for ( rowNr=1; rowNr<=NR; rowNr++ ) {
noOverlap[rowNr]
}
for ( rowNrA in rowNrs_fldVals ) {
for ( fldVal in rowNrs_fldVals[rowNrA] ) {
for ( rowNrB in fldVals_rowNrs[fldVal] ) {
if ( rowNrB > rowNrA ) {
overlap[rowNrA][rowNrB]
delete noOverlap[rowNrA]
delete noOverlap[rowNrB]
}
}
}
}
for ( rowNrA in overlap ) {
for ( rowNrB in overlap[rowNrA] ) {
print "Values overlap between lines:", rowNrA, rowNrB
}
}
for ( rowNr in noOverlap ) {
print "All unique values in line:", rowNr
}
}
$ awk -f tst.awk file
Values overlap between lines: 1 3
Values overlap between lines: 1 4
All unique values in line: 2
print "Values overlap between lines:", rowNrA, rowNrB
거기에서 나는 행을 호출하여 값이 겹치는 모든 행 사이의 모든 공통 값을 찾고 다음 PROCINFO["sorted_in"]
을 사용하여 특정 순서로 인쇄하는 (재귀 하강?) 함수를 구현해야 할 것으로 예상합니다. .
재귀 함수에 대한 정보를 문의하셨기 때문에댓글에서다음은 다양한 목적으로 사용되는 재귀 awk 함수의 예입니다(모든 함수에는 이름이 지정되지만 descend()
이름은 중요하지 않습니다).
- https://stackoverflow.com/a/46063483/1745001
- https://stackoverflow.com/a/42736174/1745001
- https://stackoverflow.com/a/32020697/1745001
- https://stackoverflow.com/a/47834902/1745001
이 작업을 통해 이러한 함수를 작성하는 방법에 대한 아이디어를 얻을 수 있기를 바랍니다.
답변2
이를 수행하는 루비는 다음과 같습니다.
ruby -e '
require "set"
line_map=Hash.new { |h,k| h[k]=[] }
num_map=Hash.new { |h,k| h[k]=Set.new() }
bucket=Hash.new { |h,k| h[k]=Set.new() }
$<.each {|line| line_all=line.chomp.split
line_all.each{|sym| line_map[sym] << $. }
num_map[$.].merge(line_all)
}
line_map.each{|k,v|
bucket[num_map[v[0]].all?{|ks| v.length==1 && line_map[ks][0]==v[0]}] << k
}
puts bucket[false].sort.join(" ")
puts bucket[true].sort.join(" ")
' file
인쇄:
a3 a7c a9 v1c v2c
a7 v5