Nối các dòng theo cột đầu tiên bằng awk hoặc sed


12

Làm thế nào tôi có thể sử dụng awktrong tình huống sau đây?

Tôi muốn nối các dòng bắt đầu với cùng một cột. Chỉ cột đầu tiên được giữ sau khi tham gia (trong trường hợp này aaa, www, hhh).

Các tập tin có thể được phân tách không gian hoặc tab.

Ví dụ đầu vào:

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

Sản phẩm chất lượng:

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

Nền tảng của điều này là tôi muốn thiết lập một cơ sở dữ liệu dựa trên tệp rất đơn giản, trong đó cột đầu tiên luôn là định danh cho thực thể. Tất cả các dòng dựa trên cùng một cột định danh được nối.


1
đâu uuudòng đến từ (trong đầu ra)?
saeedn

Xin lỗi, lỗi của tôi. Tôi sẽ chỉnh sửa nó.
nhỏ

Câu trả lời:


8

Để có được các cột đầu tiên trong mỗi dòng bằng awk, bạn có thể làm như sau:

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

Đây là chìa khóa của bạn cho phần còn lại của dòng. Vì vậy, bạn có thể tạo bảng băm, sử dụng cột đầu tiên làm khóa và cột thứ hai của dòng làm giá trị:

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

Để có được toàn bộ phần còn lại của dòng, bắt đầu với cột 2, bạn cần thu thập tất cả các cột:

< 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 

Hi, yeah nó thực sự cần phân tích để bảng băm. Cảm ơn bạn!
nhỏ

2
@tiny - Tôi đã giả định thứ tự cần thiết để được bảo tồn. Đây không phải là trường hợp (câu trả lời này tạo ra thứ tự tương ứng với cơ chế băm, không phải thứ tự ban đầu của bạn)?
ire_and_curses

3

Một số người khác có thể trả lời bằng awk hoặc sed, nhưng phiên bản Python rất đơn giản và có thể hữu ích cho bạn.

#!/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]

Rất tuyệt. Với python không có kinh nghiệm của tôi, tôi thậm chí đã quản lý để chỉnh sửa tập lệnh mà nó lấy đối số đầu tiên làm tên tệp đầu vào :)
nhỏ

2

Đây là một ứng dụng thú vị của coreutils, tôi nghi ngờ nó không hiệu quả lắm với đầu vào lớn khi nó gọi tham gia cho mỗi dòng trong đầu vào.

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

Để cải thiện hiệu quả của nó, tiết kiệm outfiletmpvào một ramdisk có thể giúp ích.

Biên tập

Hoặc không có tệp tạm thời:

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

echo "$out"

2

Và đây là một lớp lót PERL:

$ 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.