Sắp xếp tệp văn bản với nhiều dòng dưới dạng một hàng


13

Tôi có một tệp văn bản ở định dạng này:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Tôi muốn sắp xếp tệp này theo KEYdòng và giữ kết quả là 4 dòng tiếp theo để kết quả được sắp xếp phải là:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Có cách nào để làm việc này không ?


5
xin vui lòng không đăng chéo
Zanna

@Zanna: Tôi nghĩ rằng có một loại trừ cho các phần unix và Askubfox vì hai phần này có rất nhiều sự trùng lặp với nhau! Tôi nghĩ rằng tôi đã đọc về điều này trong phần meta của unix
RYN

2
câu hỏi meta có liên quan được hỏi ở đây bởi AU mod :) Làm thế nào các câu hỏi được đăng chéo trên Ask Ubuntu được xử lý?
Zanna

@RYN Vấn đề sẽ không phải là sự trùng lặp, trên thực tế có rất nhiều trang SE trùng nhau, nhưng những người đưa ra câu trả lời có thể không biết về câu trả lời trên trang khác.
phk

Câu trả lời:


12

msort(1)được thiết kế để có thể sắp xếp các tệp với các bản ghi nhiều dòng. Nó có một gui tùy chọn, cũng như một phiên bản dòng lệnh bình thường và có thể sử dụng cho con người. (Ít nhất, con người thích đọc hướng dẫn cẩn thận và tìm ví dụ ...)

AFAICT, bạn không thể sử dụng một mẫu tùy ý cho các bản ghi, vì vậy trừ khi các bản ghi của bạn có kích thước cố định (tính bằng byte, không phải ký tự hoặc dòng). msortkhông có -btùy chọn cho các bản ghi là các khối dòng được phân tách bằng các dòng trống.

Bạn có thể chuyển đổi đầu vào của mình thành một định dạng sẽ hoạt động -bkhá dễ dàng, bằng cách đặt một dòng trống trước mỗi ###...(trừ cái đầu tiên).

Theo mặc định, nó in số liệu thống kê trên stderr, vì vậy ít nhất nó cũng dễ dàng biết được khi nào nó không sắp xếp vì nó nghĩ rằng toàn bộ đầu vào là một bản ghi.


msorthoạt động trên dữ liệu của bạn. Các sedlệnh prepends một dòng mới để mỗi #+dòng trừ dòng 1. -wloại toàn bộ hồ sơ (thứ tự từ điển). Có các tùy chọn để chọn phần nào của bản ghi để sử dụng làm khóa, nhưng tôi không cần chúng.

Tôi cũng bỏ đi tước bỏ các dòng mới.

$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null 
####################################
KEY1
VAL11
VAL12
VAL13
VAL14

####################################
KEY2
VAL21
VAL22
VAL23
VAL24

####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Tôi đã không có bất kỳ may mắn nào -r '#'để sử dụng nó như là một phân tách kỷ lục. Nó nghĩ rằng toàn bộ tập tin là một bản ghi.


Cảm ơn rât nhiều; msortrất hữu ích; cảm ơn ( -rcó vẻ như là vì có nhiều hơn một # tôi đã sử dụng -dvà nó đã hoạt động
RYN

mát mẻ! (+1) msort -qwr '#' ex hoạt động với tôi (nó cũng có chức năng phân tách rec.)
JJoao

8

Một giải pháp là trước tiên thay đổi các nguồn cấp dữ liệu trong một khối thành một ký tự không sử dụng mà bạn chọn ('|' trong ví dụ bên dưới), để sắp xếp kết quả và thay đổi lại dấu phân cách đã chọn thành nguồn cấp dữ liệu gốc:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'

1
Cảm ơn; Điều này hoạt động nhưng nó rất bẩn đặc biệt khi dữ liệu quá bẩn! nếu các dòng sau khóa là 100 thì tôi cần đặt 100 ;Nở đó và khó có thể tìm thấy một ký tự không được sử dụng trong chính văn bản; nó rất tốt cho sorthoặc awk, ... có thể thực hiện phân loại đa dòng
RYN

4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 làm mờ toàn bộ tập tin
  • /(....)/g đối chiếu và trích xuất hồ sơ
  • print sort ... sắp xếp và in chúng

2

Đây là một cách khác nên hoạt động với bất kỳ số dòng nào trong một KEYphần:

# extract delimiter
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'\002' | sort -t $'\002' -k3 -k1,1 |
cut -d $'\002' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'

Điều này hoạt động bằng cách lưu dấu phân cách vào một biến (để sau đó loại bỏ nó khỏi đầu vào). Sau đó, nó nối thêm KEY*từng dòng trong phần tương ứng của nó bằng cách sử dụng char ascii thấp (không có khả năng xảy ra trong đầu vào của bạn) dưới dạng dấu phân cách và sau đó nđánh dấu tất cả các đầu vào lbằng cùng một dấu phân cách. Sau đó, chỉ là vấn đề sorting của trường thứ 3 và thứ 1 và cutnhét cột giữa và sau đó khôi phục các dấu phân cách thông qua một trận chung kết sed. Hãy lưu ý rằng với cách trên, KEY12sẽ sắp xếp trước KEY2để điều chỉnh sortlệnh theo nhu cầu của bạn.


2

Bạn có thể sử dụng thư viện stdlib POSIX Awk :

#!/usr/local/bin/awklib -f
$0 ~ "#" {x++}
{q[x] = q[x] ? q[x] RS $0 : $0}
END {
  arr_sort(q)
  for (x in q) print q[x]
}
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.