Slick one-liner để chuyển đổi một danh sách như Số 1: 2, 3, 4, 5, thành 19, 1.3, 1.4, 1.5.


7

Giả sử tôi có một tệp trông giống như thế này:

23: a, b, c, d
24: b, d, f
25: c, g

và tôi muốn có được đầu ra như thế này:

23.a
23.b
23.c
23.d
24.b
24.d
24.f
25.c
25.g

Tất nhiên không quá khó để chỉ cần đập cái gì đó ra, nhưng tôi đã tự hỏi liệu có một miếng lót trơn bóng nào đó sử dụng thứ gì đó như awk không.

Câu trả lời:


19

Có lẽ một cái gì đó như:

sed 's/: /./;s/\(\([^.]*\.\)[^,]*\), /\1\
\2/;P;D'

Đó là hai dòng ( \<LF>có thể được thay thế \nbằng một số sedtriển khai).

Các Dlệnh là một cách để thực hiện vòng lặp while trong sed. Nó loại bỏ dòng đầu tiên của không gian mẫu và miễn là có gì đó còn lại trong không gian mẫu bắt đầu lại với những gì còn lại. Vì vậy, ở trên có thể được đọc là:

do {
  - change ": " to "." so we start with "23.a, b, c"
  - change "23.x, y, z" to "23.x\n23.y, z"
  - print the first line ("23.x"): P
  - remove it
} while (pattern space is not empty)

Chúng ta không cần slệnh đầu tiên là một phần của vòng lặp, nhưng để tránh điều đó, chúng ta cần sử dụng một loại vòng lặp dài hơn như sử dụng nhãn ( :) và các lệnh phân nhánh ( b, t).


3
Đẹp, nhưng chúng tôi thích giải thích về việc mã này ...
Bananguin 24/03/13

Điều này chiến thắng cho sự thông minh tuyệt đối.
Daniel McLaury

10

Không sao, tôi chỉ nhớ chức năng chia tách awk, điều này làm cho việc này khá đơn giản.

awk -F ":" '{
  split($2, ps, ",");
  for (i in ps) {
    gsub(" ", "",ps[i]);
    print $1 "." ps[i];
  }
}'

(gsub đang tước khoảng trắng bên ngoài.)

Cảm ơn cho các câu trả lời khác, mặc dù.


Tôi đoán giống như @Stephane Chazelas, nhưng cùn hơn: awk -F ':' '{gsub (/ [^ az] /, ",", $ 2); gsub (/, + /, "\ n" $ 1 " . ", $ 2); gsub (/ ^ \ n /," ", $ 2); in $ 2} '
XzKto

1
Thông thường tôi thích phức tạp hơn FStrong các trường hợp như vậy : awk -F '[:,]' '{for(i=2;i<=NF;i++)printf"%s%s\n",$1,$i}'.
thao tác

1
Lưu ý rằng không phải tất cả các awktriển khai đều đảm bảo rằng i in psbiểu thức của bạn sẽ dẫn đến việc lặp theo thứ tự thông qua mảng. Ví dụ mawk, nhưng gawkkhông.
thao tác

Điều đó thật kỳ quái ... lợi thế nào có thể có để lặp lại theo một thứ tự khác?
Daniel McLaury

1
awkMảng là các mảng kết hợp và mảng kết hợp thường hoạt động như vậy (ví dụ HashMaptrong Java, băm trong Perl, dict trong Python, Hashtrong Ruby trước 1.9.2, mảng trong Tcl). Đó là bởi vì đại diện nội bộ của dữ liệu. Kỹ thuật phần mềm có một câu hỏi liên quan, Là một mảng giả định được đặt hàng? ,
manatwork 26/03/13

10

Đây là một Perl:

 perl -nle '/(.+?):\s*(.+)/; print "$1.$_" for split(/[,\s]+/,$2);' foo.txt

GIẢI TRÌNH:

  • perl -nle: điều này yêu cầu Perl phân tích tệp đầu vào một dòng tại một thời điểm ( -n), thực thi tập lệnh được cung cấp dưới dạng đối số -evà thêm một dòng mới ( \n) vào mỗi chuỗi được in ( -l).

  • /(.+?):\s*(.+)/: Ghép các ký tự đầu tiên cho đến dấu hai chấm đầu tiên được theo sau bởi 0 hoặc nhiều khoảng trắng ( :\s*), sau đó là phần còn lại của dòng. Các dấu ngoặc đơn là cú pháp Perl để chụp các mẫu, hai kết quả khớp được lưu dưới dạng $1$2.

  • split(/[,\s]*/,$2);: cái này sẽ phân tách $2(mẫu khớp thứ hai từ thao tác khớp ở trên) tại ,và / hoặc khoảng trắng, tạo ra một mảng ẩn danh.

  • print "$1.$_" for split(): lặp qua mảng ẩn danh được tạo bởi phần tách ở trên, lưu từng thành viên mảng $_và in nó cùng với $1(mẫu đầu tiên được chụp trong bước đầu tiên) và dấu chấm ..


Tôi đề nghị print "$1.$_\n" for ..."thay vì map { print "$1.$_\n" } ....
Christoffer Hammarström

Ngoài ra, với -lbạn không cần "\n". Nhưng có thể còn tốt hơn để sử dụng -Esay.
Christoffer Hammarström

@ Christoffer Hammarström, khuyến nghị thú vị. Lý do là gì?
thao tác

mapxây dựng và trả về một danh sách các giá trị. Ở đây nó đang được sử dụng như một forhoặc foreach.
Christoffer Hammarström

@ Christoffer Hammarström đừng quên đó saylà mới (perl> = 5.10 tôi nghĩ) và có thể không phải lúc nào cũng có sẵn. Tôi đã sử dụng mapvì đây là một lớp lót và tôi muốn nó ngắn hơn. Tôi nhận ra nó không phải là hợp pháp trên đường phố trong một bộ phận CompSci nhưng nó thực sự không tạo ra sự khác biệt trong bối cảnh này.
terdon

5

Đây là một Ruby:

ruby -ane '$F.drop(1).each{|f| puts $F.first.gsub(":",".")+f.chomp(",")}' <file.txt

Giải trình

  • ruby -ane: điều này nói với Ruby để auto phân chia các dòng, một li ne tại một thời điểm và execute đối số như một kịch bản.

  • Trong một tệp tự động phân chia $Flà một mảng của kết quả phân chia.

  • drop(1)bỏ qua trường đầu tiên (số hàng) và .eachvòng qua các trường sau.

  • gsubthay thế :chomploại bỏ một dấu phân cách từ chuỗi.


4

Một lớp lót awk mà tôi nghĩ là thanh lịch hơn một chút so với giải pháp awk khác:

awk -F'[:, ]+' '{for(i=2;i<=NF;i++)printf $1"."$i"\n"}' file.in

Nó lợi dụng thực tế là dấu tách trường awk là một biểu thức chính quy.


2

Perl:

perl -nE '($first,$rest)=split ": "; say "$first.$_" for split ", ", $rest'

Chia dòng thành số đầu tiên và phần còn lại, sau đó in "$first.$_"cho mỗi chữ cái.


2

Làm thế nào về một kịch bản shell bourne đơn giản (chủ yếu):

tr -d ':,' file.txt | while read p r; do for i in $r; do echo "$p.$i"; done; done

Lệnh "tr" chỉ xóa sạch dấu hai chấm (:) và dấu phẩy (,) - câu trả lời này dựa vào việc có khoảng trắng trong dữ liệu (mà dữ liệu mẫu có - nếu không bạn cần sử dụng sed để chuyển đổi: và, thay vào đó là khoảng trắng của tr).

Đầu ra của "tr" được dẫn vào vòng ngoài "trong khi đọc ...; do ...; xong", đọc các dòng và chia chúng thành hai, tại lần xuất hiện đầu tiên của khoảng trắng (hay đúng hơn là nội dung của "$ IFS "- dấu tách trường đầu vào shell, mặc định là khoảng trắng), để lại tiền tố trong" $ p "và phần còn lại của dòng trong" $ r ".

Vòng lặp bên trong "for i in ...; do ...; xong" sau đó phá vỡ nội dung của "$ r" tại khoảng trắng ("$ IFS") và đặt từng mục vào "$ i" trước khi thực hiện lệnh echo .

EDIT: xem bình luận - bạn hoàn toàn không cần "tr" ... dấu hai chấm và dấu phẩy có thể được làm sạch bằng cách đưa chúng vào biến IFS như vậy:

OIFS="$IFS"; IFS=":,       "; while read p r; do 
 for i in $r; do echo "$p.$i"; done; done <file.txt; IFS="$OIFS"

tất cả được thực hiện trong trình bao - không có lệnh gọi đến các chương trình bên ngoài ... (trừ khi tiếng vang không được tích hợp). Lưu ý IFS = ở trên có khoảng trắng và tab char. Cũng lưu ý rằng $ r trong vòng lặp thứ hai không có dấu ngoặc kép xung quanh nó - đây là chủ ý nên shell sẽ phân tách nó trên khoảng trắng.


Bạn có thể tr ':,' ' ' | tr -s ' '...
vonbrand

vâng - có lẽ sẽ rẻ hơn sed, nhưng bạn không cần tr thứ hai - lệnh đọc của shell sẽ coi các chuỗi khoảng trắng là một dấu tách duy nhất ... và điều này chỉ khiến tôi nghĩ - chúng ta không cần "tr" ở tất cả! Đây là một giải pháp kịch bản shell Bourne hoàn toàn: OIFS="$IFS"; IFS=":, "; while read p r; do for i in $r; do echo "$p.$i"; done; done; IFS="$OIFS"không bao giờ phải rời khỏi vỏ ... yay!
Murray Jensen
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.