Baris gabungan dengan kolom pertama dengan awk atau sed

12

Bagaimana saya dapat menggunakan awkdalam situasi berikut?

Saya ingin menyatukan garis yang dimulai dengan kolom yang sama. Hanya kolom pertama disimpan setelah bergabung (dalam hal ini aaa, www, hhh).

File mungkin dipisahkan oleh spasi atau tab.

Input contoh:

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

Output yang diinginkan:

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

Latar belakangnya adalah saya ingin membuat basis data berbasis file yang sangat sederhana, di mana kolom pertama selalu merupakan pengidentifikasi untuk entitas. Semua baris berdasarkan kolom pengidentifikasi yang sama digabungkan.

mungil
sumber
1
dari mana uuuasal (dalam output)?
saeedn
Maaf, salah saya. Saya akan mengeditnya.
kecil

Jawaban:

8

Untuk mendapatkan kolom pertama di setiap baris menggunakan awk, Anda dapat melakukan hal berikut:

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

Ini adalah kunci Anda untuk sisa baris. Jadi, Anda dapat membuat tabel hash, menggunakan kolom pertama sebagai kunci dan kolom kedua dari baris sebagai nilainya:

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

Untuk mendapatkan seluruh baris, dimulai dengan kolom 2, Anda harus mengumpulkan semua kolom:

< 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 
binfalse
sumber
Hai, ya itu benar-benar membutuhkan rincian tabel hash. Terima kasih!
kecil
2
@ kecil - Saya mengasumsikan pemesanan harus dipertahankan. Bukankah ini masalahnya (jawaban ini menghasilkan pemesanan yang sesuai dengan mekanisme hashing, bukan pesanan awal Anda)?
ire_and_curses
3

Orang lain dapat menjawab dengan awk atau sed, tetapi versi Python mudah dan mungkin membantu Anda.

#!/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]
ire_and_curses
sumber
Sangat keren. Dengan nol pengalaman python saya, saya bahkan berhasil mengedit skrip yang membutuhkan argumen pertama sebagai nama file input :)
mungil
2

Ini lebih merupakan aplikasi coreutils yang menarik, saya kira itu tidak sangat efisien dengan input besar karena ia memanggil join untuk setiap baris dalam input.

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

Untuk meningkatkan efisiensi, menabung outfiledan tmpramdisk mungkin membantu.

Edit

Atau tanpa file sementara:

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

echo "$out"
Thor
sumber
2

Dan inilah PERL one-liner:

$ perl -e 'my %h; while(<>){chomp; @a=split(/\s+/); $k=shift(@a); $h{$k}.=join(" ", @a) . " "; } map{$h{$_}=~s/\s*$//; print "$_ $h{$_}\n}keys(%hash);' infile
terdon
sumber