tr -c \\n 1 <testfile | #first transform every [^\n] char to a 1
grep -nF '' | #next get line numbers
paste -d: - testfile | #then paste it together with itself
sort -t: -nk2,2 #then sort on second field
... Và người chiến thắng là ... dòng 2, có vẻ như vậy.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Nhưng vấn đề với điều đó là mỗi dòng phải dài hơn gấp đôi để nó hoạt động - vì vậy LINE_MAX bị giảm một nửa hiệu quả. Nguyên nhân là nó đang sử dụng - cái gì, cơ sở 1? - để thể hiện độ dài của dòng. Một cách tiếp cận tương tự - và có lẽ gọn gàng hơn - có thể là nén thông tin đó trong luồng. Ý tưởng đầu tiên dọc theo những dòng xảy ra với tôi là tôi phải thực hiện unexpand
nó:
tr -c \\n \ <testfile | #transform all [^\n] to <space>
unexpand -t10 | #squeeze every series of 10 to one tab
grep -nF '' | #and get the line numbers
sed 's/:/!d;=;:/;h;:big #sed compares sequential lines
$P;$!N; /\(:[^ ]*\)\( *\)\n.*\1.*\2/!D #newest line is shorter or...
g;/:./!q;b big' | #not; quit input entirely for blank line
sed -f - -e q testfile #print only first occurrence of shortest line
Đó là bản in ...
2
4for
Một số khác, chỉ sed
:
sed -n '/^\n/D;s/\(.\)\(\n.*\)*/\1/g
$p;h; s// /g;G;x;n;//!g;H;s// /g
G; s/^\( *\)\(\n \1 *\)\{0,1\}\n//
D' <infile >outfile
Cú pháp tuân thủ các tiêu chuẩn - nhưng điều đó không đảm bảo rằng bất kỳ cái cũ nào sed
sẽ xử lý \(reference-group\)\{counts\}
chính xác - nhiều người không làm như vậy.
Về cơ bản, nó áp dụng cùng một biểu thức chính quy cho đầu vào lặp đi lặp lại - điều này có thể rất có lợi khi đến lúc biên dịch chúng. Mô hình đó là:
\(.\)\(\n.*\)*
Mà phù hợp với các chuỗi khác nhau theo những cách khác nhau. Ví dụ:
string1\nstring2\nstring3
... được khớp với s
trong \1
và ''
chuỗi null trong \2
.
1\nstring2\nstring3
... được kết hợp với 1
trong \1
và \nstring2\nstring3
trong\2
\nstring2\nstring3
... được khớp với \n
trong \1
và ''
chuỗi null trong \2
. Điều này sẽ có vấn đề nếu có bất kỳ cơ hội nào của một \n
ewline xảy ra ở phần đầu của không gian mẫu - nhưng các lệnh /^\n/D
và //!g
được sử dụng để ngăn chặn điều này. Tôi đã sử dụng [^\n]
nhưng các nhu cầu khác đối với kịch bản nhỏ này khiến tính di động trở thành mối quan tâm và tôi không hài lòng với nhiều cách nó thường bị hiểu sai. Thêm vào đó, .
là nhanh hơn.
\nstring2
string1
... khớp \n
và s
một lần nữa \1
và cả hai đều nhận được ''
chuỗi null \2
. Các dòng trống không khớp chút nào.
Khi mô hình được áp dụng theo g
chiều dọc , hai xu hướng - cả độ lệch chuẩn bên trái nhất và độ \n
lệch ewline bên phải ít hơn - được cân bằng đối nghịch để tạo ra sự bỏ qua. Một vài ví dụ:
s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g
... nếu tất cả được áp dụng (không liên tiếp) cho chuỗi sau ...
string1\nstring2
... sẽ biến nó thành ...
s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1:
: : : : : : :\nstring2
Về cơ bản, tôi sử dụng biểu thức chính quy để luôn xử lý dòng đầu tiên trong bất kỳ không gian mẫu nào mà tôi áp dụng nó. Điều đó cho phép tôi xử lý hai phiên bản khác nhau của cả hai dòng trùng khớp ngắn nhất được giữ lại và dòng gần đây nhất mà không cần dùng đến các vòng kiểm tra - mỗi lần thay thế đều xử lý toàn bộ không gian mẫu.
Các phiên bản khác nhau là cần thiết để so sánh chuỗi / chuỗi theo nghĩa đen - vì vậy phải có một phiên bản của mỗi dòng trong đó tất cả các ký tự được đảm bảo bằng nhau. Nhưng tất nhiên, nếu cái này hay cái kia thực sự là dòng ngắn nhất xuất hiện sớm nhất trong đầu vào, thì dòng được in thành đầu ra có lẽ phải là phiên bản gốc của dòng - không phải là dòng tôi đã khử trùng / đồng nhất để so sánh. Và vì vậy tôi cần hai phiên bản của mỗi.
Thật không may là một điều cần thiết khác là rất nhiều bộ đệm chuyển đổi để xử lý giống nhau - nhưng ít nhất không có bộ đệm nào vượt quá bốn dòng cần thiết để duy trì hiện tại - và vì vậy có lẽ nó không khủng khiếp.
Dù sao, đối với mỗi chu kỳ, điều đầu tiên xảy ra là một phép biến đổi trên dòng ghi nhớ - bởi vì bản sao duy nhất thực sự được lưu là bản gốc theo nghĩa đen - thành ...
^ \nremembered line$
... và sau đó, n
dòng đầu vào ext ghi đè lên bất kỳ bộ đệm cũ nào. Nếu nó không chứa ít nhất một ký tự thì nó bị bỏ qua một cách hiệu quả. Nó sẽ dễ dàng hơn nhiều chỉ đểq
ở dòng trống xuất hiện đầu tiên, nhưng, tốt, dữ liệu thử nghiệm của tôi có rất nhiều dữ liệu và tôi muốn xử lý nhiều đoạn văn.
Và vì vậy, nếu nó có chứa một ký tự, phiên bản theo nghĩa đen của nó được gắn vào dòng được nhớ và phiên bản so sánh cách nhau của nó được đặt ở đầu của không gian mẫu, như thế này:
^ \n \nremembered line\nnew$
Thay thế cuối cùng được áp dụng cho không gian mẫu đó:
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
Vì vậy, nếu dòng mới có thể vừa trong không gian cần thiết để chứa dòng đã nhớ với ít nhất một char để dự phòng thì hai dòng đầu tiên được thay thế, chỉ khác dòng đầu tiên.
Bất kể kết quả như thế nào, dòng đầu tiên trong không gian mẫu luôn luôn bị D
xóa ở cuối chu kỳ trước khi bắt đầu lại. Điều này có nghĩa là nếu dòng mới ngắn hơn chuỗi cuối ...
new
... được gửi trở lại thay thế đầu tiên trong chu kỳ sẽ luôn chỉ thoát khỏi char dòng mới đầu tiên trên - và vì vậy nó vẫn còn nguyên. Nhưng nếu không thì chuỗi ...
remembered line\nnew
... thay vào đó sẽ bắt đầu chu kỳ tiếp theo và thay thế đầu tiên sẽ loại bỏ chuỗi ...
\nnew
...mỗi lần.
Trên dòng cuối cùng, dòng ghi nhớ được in ra theo tiêu chuẩn, và vì vậy đối với dữ liệu mẫu được cung cấp, nó sẽ in:
4for
Nhưng, nghiêm túc, sử dụng tr
.