Thay thế chuỗi bằng chỉ mục tuần tự


9

Ai đó có thể đề nghị một cách thanh lịch để thực hiện điều này?

Đầu vào:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

đầu ra phải là:

test      instant1  ()

test      instant2  ()

test      instant1000()

Các dòng trống nằm trong các tệp đầu vào của tôi và có nhiều tệp trong cùng thư mục mà tôi cần xử lý cùng một lúc.

Tôi đã thử điều này để thay thế nhiều tệp trong cùng một thư mục và không hoạt động.

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

lỗi:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

và tôi cũng đã thử điều này: perl -i -pe 's/instant/$& . ++$n/ge' *.vs

Nó hoạt động nhưng chỉ mục cứ tăng dần từ tệp này sang tệp khác. Tôi muốn đặt lại thành 1 cho tệp diff. Bất kỳ đề nghị tốt?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

hoạt động nhưng nó đã thay thế tất cả các tập tin khác không nên thay thế. Tôi chỉ muốn thay thế các tệp bằng "* .txt" mà thôi.


Và tất cả chúng chỉ bao gồm các dòng trống hay test instant ()?
terdon

Tôi đặt các dòng đôi cách nhau trở lại, chúng thường là dấu hiệu của người dùng mới không biết cách sử dụng đánh dấu trang web này, đó là lý do tại sao terdon xóa chúng trong khi thụt lề chính xác khối nội dung tệp của bạn để nó hiển thị dưới dạng nội dung tệp. Hy vọng nó ổn bây giờ.
Timo

Câu trả lời:


13
perl -pe 's/instant/$& . ++$n/ge'

hoặc với GNU awk:

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

Để chỉnh sửa các tập tin tại chỗ, thêm -itùy chọn vào perl:

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*

Hoặc đệ quy:

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

Giải thích

perl -pe 's/instant/$& . ++$n/ge'

-plà xử lý dòng đầu vào theo dòng, đánh giá biểu thức được truyền -echo mỗi dòng và in nó. Đối với mỗi dòng, chúng tôi thay thế (sử dụng s/re/repl/flagstoán tử) instantcho chính nó ( $&) và giá trị tăng của một biến ++$n. Các glá cờ là làm cho việc thay thế toàn cầu (không chỉ một lúc), và eđể thay thế được hiểu như mã perl tới e định giá trị (không phải là một chuỗi cố định).

Để chỉnh sửa tại chỗ trong đó một lần gọi perl xử lý nhiều hơn một tệp, chúng tôi muốn $nđặt lại ở mỗi tệp. Thay vào đó, chúng tôi sử dụng $n{$ARGV}(nơi $ARGVtập tin hiện đang được xử lý).

Một awkngười xứng đáng một chút giải thích.

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

Chúng tôi đang sử dụng khả năng của GNU awkđể phân tách các bản ghi trên các chuỗi tùy ý (thậm chí cả biểu thức chính quy). Với -vRS=instant, chúng tôi đặt bộ điều khiển r̲ecord thành instant. RTlà biến chứa những gì được khớp bởi RS, vì vậy, thông thường, instantngoại trừ bản ghi cuối cùng trong đó nó sẽ là chuỗi rỗng. Trong đầu vào ở trên các bản ghi ( $0) và các đầu cuối bản ghi ( RT) là ( [$0|RT]):

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

Vì vậy, tất cả những gì chúng ta cần làm là chèn một số tăng dần vào đầu mỗi bản ghi trừ bản đầu tiên.

Đó là những gì chúng tôi làm ở trên. Đối với bản ghi đầu tiên, nsẽ trống. Chúng tôi đặt ORS (bộ điều khiển output r̲ecord ) thành RT, để awk in n $0 RT. Nó thực hiện theo biểu thức thứ hai ( ++n) là điều kiện luôn luôn đánh giá là đúng (số khác không), và do đó, hành động mặc định (in $0 ORS) được thực hiện cho mọi bản ghi.



4

sedthực sự không phải là công cụ tốt nhất cho công việc, bạn muốn một cái gì đó có khả năng viết kịch bản tốt hơn. Dưới đây là một số lựa chọn:

  • perl

    perl -000pe 's/instant/$& . $./e' file 

    -pnghĩa là "in mọi dòng" sau khi áp dụng bất kỳ tập lệnh nào được đưa ra -e. Việc -000bật "chế độ đoạn" để các bản ghi (dòng) được xác định bởi các ký tự dòng mới ( \n) liên tiếp , điều này cho phép nó xử lý các dòng cách đôi chính xác. $&là mẫu cuối cùng được khớp và $.là số dòng hiện tại của tệp đầu vào. Việc ein s///echo phép tôi đánh giá các biểu thức trong toán tử thay thế.

  • awk (điều này giả sử dữ liệu của bạn chính xác như được hiển thị, với ba trường được phân tách bằng dấu cách)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    Ở đây, chúng tôi chỉ tăng kbiến knếu dòng hiện tại không trống /./trong trường hợp chúng tôi cũng in thông tin cần thiết. Dòng trống được in như là.

  • vỏ khác nhau

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 

    Ở đây, mỗi dòng đầu vào được tự động phân chia trên khoảng trắng và các trường được lưu dưới dạng $a, $b$c. Sau đó, trong vòng lặp, $cđược tăng thêm một cho mỗi dòng $akhông trống và giá trị hiện tại của nó được in bên cạnh trường thứ hai , $b.

LƯU Ý: tất cả các giải pháp trên cho rằng tất cả các dòng trong tệp có cùng định dạng. Nếu không, câu trả lời của @ Stephane là cách để đi.


Để xử lý nhiều tệp và giả sử rằng bạn muốn làm điều này với tất cả các tệp trong thư mục hiện tại, bạn có thể sử dụng:

for file in ./*; do perl -i -000pe 's/instant/$& . $./e' "$file"; done

CẨN THẬN: Đó là giả định tên tập tin đơn giản không có dấu cách, nếu cần thiết để đối phó với một cái gì đó phức tạp hơn, đi cho (giả định ksh93, zshhoặc bash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -000pe 's/instant/$& . $./e' "$file"
done

kịch bản perl hoạt động. tuy nhiên có một vấn đề nhỏ nếu các dòng là không gian kép.
dùng3342338

@ user3342338 có, điều đó sẽ tăng bộ đếm vì tôi đang sử dụng số dòng hiện tại. Đây là một cách tiếp cận rất ngây thơ, như tôi đã nói Stephane mạnh mẽ hơn. Không có cái nào trong số này hoạt động nếu bạn có dòng trống hoặc nếu bất kỳ dòng nào của bạn lệch khỏi những gì bạn hiển thị.
terdon

@ user3342338 xem câu trả lời cập nhật. Bây giờ tất cả chúng sẽ hoạt động cho các tệp khoảng cách gấp đôi.
terdon

Câu trả lời tuyệt vời và lựa chọn phương pháp thay thế !! Cảm ơn
Madivad
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.