Làm thế nào để chỉ thay thế sự xuất hiện thứ N của một mẫu trong một tệp?


10

Làm thế nào để thay thế lần xuất hiện thứ ba của chuỗi trong tệp bằng sedlệnh.

Thí dụ:

Chỉ thay đổi sự xuất hiện thứ ba của isđể ustrong file.

Tệp đầu vào của tôi chứa:

hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged.

Tôi đang mong đợi đầu ra là:

hai this is linux.
hai thus is unix.
hai this is mac.
hai this is unchanged.

3
Đầu vào và đầu ra là như nhau.
Hauke ​​Laging

4
sedkhông phải là công cụ phù hợp cho công việc.
choroba

@don_crissti Mình đã sửa rồi. OP đã không sử dụng các công cụ định dạng (nhân tiện, Sureshkumar, xem tại đây để được trợ giúp chỉnh sửa câu hỏi của bạn) và các biên tập viên kế tiếp đã hiểu sai những gì muốn.
terdon

Câu trả lời:


11

Nó dễ dàng hơn nhiều với perl.

Để thay đổi lần xuất hiện thứ 3 :

perl -pe 's{is}{++$n == 3 ? "us" : $&}ge'

Để thay đổi mỗi lần xuất hiện thứ 3 :

perl -pe 's{is}{++$n % 3 ? $& : "us"}ge'

3

Khi chuỗi thay thế chỉ xảy ra một lần trên mỗi dòng, bạn có thể kết hợp các tiện ích khác nhau.
Khi đầu vào nằm trong tệp "đầu vào" và bạn đang thay thế "là" bằng "chúng tôi", bạn có thể sử dụng

LINENR=$(cat input | grep -n " is " | head -3 | tail -1 | cut -d: -f1)
cat input | sed ${LINENR}' s/ is / us /'

Trong ví dụ trong câu hỏi, có nhiều hơn một isdòng trên mỗi dòng.
terdon

Tôi nghĩ rằng bạn đang tìm kiếm "là" với không gian. Tôi có thể chỉnh sửa câu trả lời của mình bằng lệnh tr như @jimmij đã sử dụng, nhưng giải pháp của tôi sẽ trở nên kém xa với anh ta.
Walter A

Tôi không phải là người hỏi :). Tôi nghĩ điều tương tự, đó là lý do tôi đã bỏ phiếu tán câu trả lời của bạn, nhưng nếu bạn nhìn vào phiên bản gốc của câu hỏi (click vào "phút Edited X trước" liên kết), bạn sẽ thấy rằng OP dự kiến trong này được thay đổi thành như vậy . Nhân tiện, không cần mèo ở đó.
terdon

2

Tập lệnh bên dưới (sử dụng cú pháp GNU sed ) có thể sử dụng để chỉnh sửa tại chỗ không phải cho đầu ra vì nó dừng các dòng in sau khi thay thế mong muốn:

sed -i '/is/{: 1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; q}' text.file

Nếu quyết định thích choroba của bạn, bạn có thể sửa đổi ở trên thành

sed '/is/{:1 ; /\(.*is\)\{3\}/!{N;b1} ; s/is/us/3 ; :2 ; n ; $!b2}' text.file

đầu ra tất cả các dòng

Hoặc bạn phải đặt tất cả các dòng trong không gian mẫu (trong bộ nhớ, vì vậy hãy cẩn thận với giới hạn kích thước) và thay thế

sed ': 1 ; N ; $!b1 ; s/is/us/3 ' text.file

2

Bạn có thể sử dụng sedcho điều đó nếu các dòng mới trước đó được thay thế cho bất kỳ ký tự nào khác, ví dụ:

tr '\n' '\000' | sed 's/is/us/3' | tr '\000' '\n'

Và tương tự với thuần (GNU) sed:

sed ':a;N;$!ba;s/\n/\x0/g;s/is/us/3;s/\x0/\n/g'

( sedthay thế dòng mới một cách đáng xấu hổ bị đánh cắp từ /programming//a/1252191/4488514 )


Nếu bạn sẽ sử dụng sedcú pháp cụ thể của GNU , bạn cũng có thể sử dụng sed -z 's/is/us/3'.
Stéphane Chazelas

@ StéphaneChazelas -zphải là một tính năng hoàn toàn mới, tôi GNU sed version 4.2.1không biết gì về tùy chọn này.
jimmij

1
Đã thêm vào 4.2.2 (2012). Trong giải pháp thứ hai của bạn, bạn không cần chuyển đổi sang \x0bước.
Stéphane Chazelas

Xin lỗi về chỉnh sửa. Tôi đã không thấy phiên bản gốc của câu hỏi và ai đó đã hiểu nhầm và chỉnh sửa sai dòng. Tôi trở lại phiên bản trước.
terdon

1
p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\}            \
     -e's/(\n*)(.*)/ \2 \1/'       \
     -e"s/is[$p]?[$s]/\n&/g"       \
     -e"s/([^$s])\n/\1/g;1G"       \
-e:c -e"s/\ni(.* )\n{3}/u\1/"      \
     -e"/\n$/!s/\n//g;/\ni/G"      \
     -e's//i/;//tc'                \
     -e's/^ (.*) /\1/;P;$d;N;D'

Đó sedchỉ là một phần của issự xuất hiện từ dòng này sang dòng tiếp theo. Nó đáng tin cậy sẽ xử lý nhiều ises mỗi dòng khi bạn ném vào nó và nó không cần phải đệm các dòng cũ trong khi nó - nó chỉ giữ lại một ký tự dòng mới cho mỗi iscâu mà nó không phải là một phần của từ khác.

Kết quả cuối cùng là nó sẽ chỉ sửa đổi lần xuất hiện thứ ba trong một tệp - và nó sẽ mang số đếm trên mỗi dòng. Vì vậy, nếu một tập tin trông giống như:

1. is is isis
2. is does

... nó sẽ in ...

1. is is isis
2. us does

Đầu tiên, nó xử lý các trường hợp cạnh bằng cách chèn một khoảng trắng ở đầu và đuôi của mỗi dòng. Điều này làm cho ranh giới từ dễ dàng hơn một chút để xác định.

Tiếp theo, nó sẽ tìm ises hợp lệ bằng cách chèn một \newline trước tất cả các lần xuất hiện isngay trước 0 hoặc một ký tự dấu chấm theo sau là khoảng trắng . Nó thực hiện một lần nữa và loại bỏ tất cả các \newlines ngay trước một ký tự không phải không gian. Điểm đánh dấu này để lại sẽ phù hợp is.isnhưng không thishoặc ?is.

Nó tiếp theo tập hợp mỗi điểm đánh dấu vào đuôi của chuỗi - cho mỗi \nitrận đấu trên một dòng, nó sẽ thêm một \newline vào đuôi của chuỗi và thay thế nó bằng một ihoặc u. Nếu có 3 \newlines liên tiếp được tập hợp ở đuôi chuỗi thì nó sử dụng u - khác là i. Lần đầu tiên au được sử dụng cũng là lần cuối cùng - sự thay thế đặt ra một vòng lặp vô hạn, sôi sục lên get line, print line, get line, print line,và cứ thế.

Vào cuối mỗi chu kỳ vòng thử, nó sẽ dọn sạch các khoảng trắng được chèn, chỉ in đến dòng mới xuất hiện đầu tiên trong không gian mẫu và đi lại.

Tôi sẽ thêm một llệnh ook ở đầu vòng lặp như:

l; s/\ni(.* )\n{9}/u\1/...

... và hãy xem những gì nó làm khi nó hoạt động với đầu vào này:

hai this is linux.
hai this is unix.


hai this is mac.
hai this is unchanged is.

... đây là những gì nó làm:

 hai this \nis linux. \n$        #behind the scenes
hai this is linux.               #actually printed
 hai this \nis unix. \n\n$       #it builds the marker string
hai this is unix.
  \n\n\n$                        #only for lines matching the

  \n\n\n$                        #pattern - and not otherwise.

 hai this \nis mac. \n\n\n$      #here's the match - 3 ises so far in file.
hai this us mac.                 #printed
hai this is unchanged is.        #no look here - this line is never evaled

Nó có ý nghĩa hơn có thể với nhiều ises trên mỗi dòng:

nthword()(  p='[:punct:]' s='[:space:]'         
    sed -e '1!{/\n/!b' -e\}             \
        -e 's/\(\n*\)\(.*\)/ \2 \1/'    \
        -e "s/$1[$p]\{0,1\}[$s]/\n&/g"  \
        -e "s/\([^$s]\)\n/\1/g;1G;:c"   \
        -e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
        -e '/\n$/!s/\n//g;/\n'"$1/G"    \
        -e "s//$1/;//tc" -e 's/^ \(.*\) /\1/'     \
        -e 'P;$d;N;D'
)        

Đó thực tế là điều tương tự nhưng được viết bằng w / POSIX BRE và xử lý đối số thô sơ.

 printf 'is is. is? this is%.0s\n' {1..4}  | nthword is us 12

...được...

is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is

... và nếu tôi kích hoạt ${dbg}:

printf 'is is. is? this is%.0s\n' {1..4}  | 
dbg=1 nthword is us 12

... chúng ta có thể xem nó lặp đi lặp lại ...

 \nis \nis. \nis? this \nis \n$
 is \nis. \nis? this \nis \n\n$
 is is. \nis? this \nis \n\n\n$
 is is. is? this \nis \n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
 \nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
 is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
 is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
 is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is

Bạn có nhận ra ví dụ của bạn nói "isis" không?
flarn2006

@ flarn2006 - tôi khá chắc chắn rằng nó nói là như vậy.
mikeerv

0

Đây là một giải pháp logic sử dụng sedtrnhưng phải được viết bằng một kịch bản để nó hoạt động. Mã dưới đây thay thế mỗi lần xuất hiện thứ 3 của từ được chỉ định trong sedlệnh. Thay thế i=3bằng i=nđể làm cho công việc này cho bất kỳ n.

Mã số:

# replace new lines with '^' character to get everything onto a single line
tr '\n' '^' < input.txt > output.txt

# count number of occurrences of the word to be replaced
num=`grep -o "apple" "output.txt" | wc -l`

# in successive iterations, replace the i + (n-1)th occurrence
n=3
i=3
while [ $i -le $num ]
do
    sed -i '' "s/apple/lemon/${i}" 'output.txt'
    i=$(( i + (n-1) ))
done

# replace the '^' back to new line character
tr '^' '\n' < output.txt > tmp && mv tmp output.txt


Tại sao điều này hoạt động:

Giả sử tập tin văn bản là a b b b b a c a d a b b b a b e b z b s b a b.

  • Khi n = 2: chúng tôi muốn thay thế mỗi lần xuất hiện thứ hai của b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . ^ . ^ . . . . . . ^ . . ^ . . . ^ . ^ . ^
    • Đầu tiên chúng ta thay thế lần xuất hiện thứ 2, sau đó là lần xuất hiện thứ 3, sau đó là lần thứ 4, thứ 5, v.v. Đếm trong chuỗi hiển thị ở trên để thấy điều này cho chính mình.
  • Khi n = 3: chúng tôi muốn thay thế mỗi lần xuất hiện thứ ba của b.

    • a b b b b a c a d a b b b a b e b z b s b a b
      . . . ^ . . . . . . . ^ . . . . ^ . . . . . ^
    • Đầu tiên chúng ta thay thế lần xuất hiện thứ 3, sau đó là lần thứ 5, sau đó là lần thứ 7, 9, 11, v.v.
  • Khi n = 4: chúng tôi muốn thay thế mỗi lần xuất hiện thứ ba của b.

    • Đầu tiên chúng ta thay thế lần thứ 4, rồi thứ 7, rồi thứ 10, ngày 13, v.v.
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.