2013-01-25 28 views

Trả lời

5

Tôi sẽ làm điều đó bằng cách tìm số dòng trước khi chạy tập lệnh, ví dụ: với coreutils và bash:

awk -v nlines=$(wc -l < $a) '{printf "%s", $1"-"$2} NR != nlines { printf ", " }' $a >>positions 

Nếu tệp của bạn chỉ có 2 cột, thay thế lõi sau đây cũng hoạt động. Ví dụ dữ liệu:

paste <(seq 5) <(seq 5 -1 1) | tee testfile 

Output:

1 5 
2 4 
3 3 
4 2 
5 1 

Bây giờ thay thế tab với dòng mới, paste dễ dàng lắp ráp ngày vào định dạng mong muốn:

<testfile tr '\t' '\n' | paste -sd-, 

Output:

1-5,2-4,3-3,4-2,5-1 
+0

Cảm ơn bạn, đúng vậy! – Perlnika

+0

FYI, việc sử dụng 'tr' và' paste' này sẽ hoạt động trong bất kỳ môi trường POSIX nào, nó không bị hạn chế đối với các lõi của GNU. – ghoti

1

Đây là một cách tốt hơn, mà không cần đến coreutils:

awk 'FNR==NR { c++; next } { ORS = (FNR==c ? "\n" : ", "); print $1, $2 }' OFS="-" file file 
+0

Tại sao điều này tốt hơn so với Thor? Khi nào bạn sẽ có 'awk' nhưng không phải là 'wc'? –

+0

@ MichaelJ.Barber: Cài đặt bị hỏng là câu trả lời ngắn. Bất kể, nó là awkish hơn để chỉ đơn giản là viết một số mã mà giả lập 'wc -l Steve

+1

Ngoài ra, Thor đã sử dụng hai báo cáo in khi chỉ có một là thực sự cần thiết. Anh ấy đã thêm một sửa chữa nhanh chóng, khi những gì được yêu cầu là viết lại. HTH. – Steve

19

Độc thân qua cách tiếp cận:

cat "$a" | # look, I can use this in a pipeline! 
    awk 'NR > 1 { printf(", ") } { printf("%s-%s", $1, $2) }' 

Lưu ý rằng tôi cũng đã đơn giản hóa các định dạng chuỗi.

+1

Nhìn này, bạn cũng có thể sử dụng «awk <" $ a "'Nr ...'' và nó sẽ hoạt động trong một quá trình (không có mèo)! –

+1

@ KrzysztofJabłoński Tôi nhận ra rằng một hận thù cuồng tín khi sử dụng 'cat' là de rigueur ở đây, nhưng thay vào đó bạn lại bỏ lỡ điểm hoàn toàn. Các câu trả lời khác phụ thuộc vào cách tiếp cận đa pass không cho phép sử dụng trong một đường ống mà tôi đã trình bày ở đây để có thể. –

+0

Tôi đồng ý rằng điểm dự định là hiển thị mức sử dụng trong đường ống. Dường như tôi không đồng ý về cách sử dụng 'cat' cho mục đích đó. Tôi thường thấy rằng lệnh khiêm tốn được khai thác để bắt đầu một đường ống dẫn. Để minh họa cho ý tưởng, tôi muốn hiển thị một cái gì đó như 'dataGeneratorCmd | dataFilterCmd | awk '...' | terminalConsumerCmd'. Nhưng đó là vấn đề về sở thích và sở thích. Dù sao, đừng cảm thấy bị xúc phạm. Đây vẫn là một câu trả lời khá toàn diện. +1 –

0
awk '{a[NR]=$1"-"$2;next}END{for(i=1;i<NR;i++){print a[i]", " }}' $a > positions 
9

Thưởng thức này một:

awk '{printf t $1"-"$2} {t=", "}' $a >> positions 

Yeh, trông hơi khó khăn ngay từ cái nhìn đầu tiên. Vì vậy, tôi sẽ giải thích, trước hết chúng ta hãy thay đổi printf vào print cho rõ ràng:

awk '{print t $1"-"$2} {t=", "}' file 

và có một cái nhìn những gì nó làm, ví dụ, đối với tập tin với nội dung đơn giản này:

1 A 
2 B 
3 C 
4 D 

nên nó sẽ tạo ra những điều sau đây:

1-A 
, 2-B 
, 3-C 
, 4-D 

Bí quyết là biến số t trước đó trống vào đầu. Biến sẽ được đặt {t=...} chỉ vào bước xử lý tiếp theo sau khi được hiển thị {print t ...}. Vì vậy, nếu chúng tôi (awk) tiếp tục lặp lại, chúng tôi sẽ nhận được chuỗi mong muốn.

+0

Câu trả lời của bạn xuất hiện trong hàng đợi đánh giá chất lượng thấp. Vui lòng chỉnh sửa câu trả lời của bạn để bao gồm mô tả về cách hoạt động của tính năng này. –

+0

Giải pháp IMHO này tốt hơn câu trả lời được chấp nhận vì số lượng các dòng đôi khi không thể hoặc khó tính toán trước khi bị lúng túng. Ví dụ trong trường hợp tôi muốn wak thoát nếu dòng trống gặp phải – leftjoin

+0

Điều này rất hay, nhưng nếu bạn in các dòng dựa trên điều kiện bỏ qua một số dòng tiêu đề, nó sẽ không hoạt động kể từ sau khi bỏ qua dòng đầu tiên, 't' gots giao. Nó hoạt động sau đó bằng cách sử dụng điều kiện tương tự cho '{t =", "}' nhưng nó có vẻ hơi bẩn đối với tôi. Ví dụ: '$ 1 ~/^ [0-9] * $/{in t $ 1} $ 1 ~/^ [0-9] * $/{t =", "}' không có giải pháp nào sạch hơn? – r1verside

1

Bạn có thể nghĩ rằng ORS và OFS awk của sẽ là một cách hợp lý để xử lý này:

$ awk '{print $1,$2}' OFS="-" ORS=", " input.txt 

Nhưng kết quả này trong một ORS thức vì đầu vào chứa một dòng mới trên dòng cuối cùng. Dòng mới là một dấu phân cách bản ghi, do đó, từ quan điểm của awk có một bản ghi cuối cùng trống trong đầu vào.Bạn có thể làm việc xung quanh điều này với một chút hackery, nhưng kết quả phức tạp loại bỏ sự sang trọng của một lớp lót.

Vì vậy, đây là cách tôi thực hiện việc này. Vì bạn nói bạn đang "viết nhiều giá trị cột", có thể việc mucking bằng ORS và OFS sẽ gây ra sự cố. Vì vậy, chúng tôi có thể đạt được sản lượng mong muốn hoàn toàn với định dạng.

$ cat input.txt 
3 2 
5 4 
1 8 
$ awk '{printf "%s%d-%d",t,$1,$2; t=", "} END{print ""}' input.txt 
3-2, 5-4, 1-8 

Điều này tương tự như cách tiếp cận một lần của Michael và rook, nhưng nó sử dụng một đơn lẻ để định dạng.

Điều này có thể sẽ hoạt động tốt hơn đáng kể so với giải pháp của Michael vì một bài tập nên lấy ít CPU hơn bài kiểm tra và đáng chú ý hơn bất kỳ giải pháp multi-pass nào vì tệp chỉ cần đọc một lần.

Các vấn đề liên quan