Chèn dòng sau trận đấu đầu tiên bằng sed


245

Vì một số lý do, tôi dường như không thể tìm thấy một câu trả lời thẳng thắn cho vấn đề này và hiện tại tôi đang hơi khó khăn. Làm thế nào tôi có thể chèn một dòng văn bản lựa chọn sau khi dòng đầu tiên khớp với một chuỗi cụ thể bằng cách sử dụng sedlệnh. Tôi có ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

Và tôi muốn chèn một dòng sau CLIENTSCRIPT=dòng dẫn đến ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Câu trả lời:


379

Hãy thử làm điều này bằng GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

nếu bạn muốn thay thế tại chỗ , hãy sử dụng

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

Đầu ra

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Bác sĩ


Cảm ơn! Và để có được nó để ghi lại vào tập tin mà không in nội dung của tập tin?
dùng2150250

11
Lưu ý rằng cả hai đều giả sử GNU sed. Đó không phải là sedcú pháp chuẩn và sẽ không hoạt động với bất kỳ sedtriển khai nào khác .
Stephane Chazelas

Tôi gặp khó khăn khi làm việc này cho tôi vì tôi đang sử dụng một dấu phân cách khác. Sau RTFM'ing, tôi nhận ra rằng tôi phải bắt đầu regex bằng một \, sau đó là dấu phân cách.
Christy

10
Làm thế nào để nó áp dụng CHỈ cho trận đấu đầu tiên? Rõ ràng là nó nối thêm văn bản sau trận đấu, nhưng làm thế nào để nó chỉ biết đến trận đấu đầu tiên?
Mohammed Noureldin

7
Điều này thêm văn bản sau mỗi trận đấu, đối với tôi.
schoppenhauer

49

Lưu ý tiêu chuẩn sed cú pháp (như trong POSIX, do đó được hỗ trợ bởi tất cả các sedtriển khai tuân thủ xung quanh (GNU, OS / X, BSD, Solaris ...)):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

Hoặc trên một dòng:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

(-e xpressions (và nội dung của -files) được nối với các dòng mới để tạo nên các trình sedthông dịch kịch bản sed ).

Các -itùy chọn để chỉnh sửa tại chỗ cũng là một phần mở rộng GNU, một số hiện thực khác (như FreeBSD) hỗ trợ -i ''cho điều đó.

Ngoài ra, đối với tính di động, bạn có thể sử dụng perl thay thế:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

Hoặc bạn có thể sử dụng edhoặc ex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
Tôi có thể sai, nhưng hiện tại sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' filethoát khỏi trích dẫn ở cuối tham số đầu tiên và phá vỡ lệnh.
Giỏ hàng bị bỏ rơi

1
@AbandnameCart, trong vỏ của Bourne, cshhoặc rcgia đình, '...'là những trích dẫn mạnh mẽ bên trong mà dấu gạch chéo ngược không đặc biệt. Ngoại lệ duy nhất mà tôi biết là fishvỏ.
Stephane Chazelas

1
Terminal trên MacOS (và sau đó là một đoạn script chạy trong terminal), cũng là một ngoại lệ. Tôi tìm thấy cú pháp thay thế, nhưng dù sao cũng cảm ơn.
Giỏ hàng bị bỏ rơi

1
@AbandnameCart, đó là một cái gì đó khác. Đó là macOS sedkhông tuân thủ POSIX ở đây. Điều đó làm cho tuyên bố của tôi về nó là di động không chính xác (đối với biến thể một dòng). Tôi sẽ yêu cầu opengroup xác nhận nếu đó thực sự là một sự không phù hợp hoặc giải thích sai về tiêu chuẩn từ phía tôi.
Stephane Chazelas

19

Một tương thích POSIX sử dụng slệnh:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
... mà thậm chí còn hỗ trợ chèn các dòng mới. Tôi thích nó!
Stephan Henningsen

1
Xem thêm sed -e '/.../s/$/\' -e 'CLI.../'để tránh các vấn đề với các dòng chứa chuỗi byte không tạo thành các ký tự hợp lệ.
Stephane Chazelas

14

Lệnh sed hoạt động trên MacOS (ít nhất là OS 10) và Unix giống nhau (nghĩa là không yêu cầu gnu sed như Gilles '(hiện được chấp nhận).

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

Điều này hoạt động trong bash và có thể các shell khác cũng biết kiểu trích dẫn đánh giá $ '\ n'. Mọi thứ có thể nằm trên một dòng và hoạt động trong các lệnh sed / POSIX cũ hơn. Nếu có thể có nhiều dòng khớp với CLIENTSCRIPT = "foo" (hoặc tương đương của bạn) và bạn chỉ muốn thêm dòng bổ sung vào lần đầu tiên, bạn có thể làm lại như sau:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(điều này tạo ra một vòng lặp sau mã chèn dòng chỉ quay vòng qua phần còn lại của tệp, không bao giờ quay lại lệnh sed đầu tiên nữa).

Bạn có thể nhận thấy tôi đã thêm '^ *' vào mẫu phù hợp trong trường hợp dòng đó hiển thị trong một nhận xét, nói hoặc được thụt lề. Nó không hoàn hảo 100% nhưng bao gồm một số tình huống khác có thể là phổ biến. Điều chỉnh theo yêu cầu ...

Hai giải pháp này cũng giải quyết được vấn đề (đối với giải pháp chung để thêm một dòng) rằng nếu dòng chèn mới của bạn chứa dấu gạch chéo ngược hoặc ký hiệu, chúng sẽ được giải thích bởi sed và có thể không xuất hiện giống nhau, giống như \nlà - vd.\0sẽ là dòng đầu tiên phù hợp. Đặc biệt tiện dụng nếu bạn thêm một dòng xuất phát từ một biến trong đó trước tiên bạn phải thoát mọi thứ bằng cách sử dụng $ {var //} hoặc một câu lệnh sed khác, v.v.

Giải pháp này ít lộn xộn hơn trong các tập lệnh (mặc dù trích dẫn và \ n không dễ đọc), khi bạn không muốn đặt văn bản thay thế cho lệnh khi bắt đầu một dòng nếu nói, trong một hàm với các đường lõm. Tôi đã lợi dụng rằng $ '\ n' được đánh giá theo dòng mới bởi shell, nó không nằm trong các giá trị trích dẫn đơn '\ n' thông thường.

Nó đủ dài mặc dù tôi nghĩ rằng perl / thậm chí awk có thể thắng do dễ đọc hơn.


1
Cảm ơn bạn đã trả lời! Đầu tiên hoạt động như một nét duyên dáng trên hệ điều hành AIX.
abhishek

Làm thế nào để bạn làm điều này nhưng để chèn nhiều dòng?
tatsu

1
POSIX sed hỗ trợ các dòng mới theo nghĩa đen trong chuỗi thay thế nếu bạn "thoát" chúng bằng dấu gạch chéo ngược ( nguồn ). Vì vậy: s/CLIENTSCRIPT="foo"/&\ [Nhập] CLIENTSCRIPT2="hello"/ . Dấu gạch chéo ngược phải là ký tự cuối cùng trên dòng (không có khoảng trắng sau nó), trái với giao diện của nó trong nhận xét này.
TheDudeAdides 6/12/19

1
@tatsu Các dòng mới trong chuỗi thay thế s///, cũng như các lệnh trong ailệnh có thể được "thoát", sử dụng cùng một phương pháp tôi mô tả trong nhận xét của tôi ở trên.
TheDudeAdides 6/12/19

4

Có thể hơi muộn để đăng một câu trả lời cho điều này, nhưng tôi thấy một số giải pháp trên hơi rườm rà.

Tôi đã thử thay thế chuỗi đơn giản trong sed và nó hoạt động:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

& dấu hiệu phản ánh chuỗi phù hợp và sau đó bạn thêm \ n và dòng mới.

Như đã đề cập, nếu bạn muốn thực hiện tại chỗ:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

Cái khác. Bạn có thể kết hợp bằng biểu thức:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

Hy vọng điều này sẽ giúp ai đó


Điều này hoạt động tốt trên Linux, trong đó GNU sed là mặc định, vì nó hiểu \ntrong chuỗi thay thế. Plain-vani (POSIX) sedkhông không hiểu \ntrong chuỗi thay thế, tuy nhiên, vì vậy điều này sẽ không làm việc trên BSD hoặc hệ điều hành MacOS-trừ khi bạn đã cài đặt GNU sed bằng cách nào đó. Với vanilla sed, bạn có thể nhúng dòng mới bằng cách "thoát" chúng, nghĩa là kết thúc dòng bằng dấu gạch chéo ngược, sau đó nhấn [Enter] ( tham chiếu , dưới tiêu đề cho [2addr]s/BRE/replacement/flags). Điều này có nghĩa là lệnh sed của bạn phải trải dài trên nhiều dòng trong terminal.
TheDudeAdides

Một tùy chọn khác để có được một dòng mới theo nghĩa đen trong chuỗi thay thế là sử dụng tính năng trích dẫn ANSI-C trong Bash, như được đề cập trong một trong những câu trả lời khác .
TheDudeAdides


1

Tôi đã có một nhiệm vụ tương tự, và không thể làm cho giải pháp perl ở trên hoạt động.

Đây là giải pháp của tôi:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

Giải trình:

Sử dụng một biểu thức thông thường để tìm kiếm một dòng trong tệp /etc/mysql/my.cnf của tôi chỉ chứa [mysqld]và thay thế nó bằng

[mysqld] collation-server = utf8_unicode_ci

có hiệu quả thêm collation-server = utf8_unicode_cidòng sau dòng chứa [mysqld].


0

Gần đây tôi đã phải làm điều này cho cả Mac và Linux OS và sau khi duyệt qua nhiều bài đăng và thử nhiều thứ, theo ý kiến ​​cụ thể của tôi, tôi chưa bao giờ đến nơi tôi muốn: đó là một giải pháp đủ đơn giản để hiểu bằng cách sử dụng nổi tiếng và các lệnh tiêu chuẩn với các mẫu đơn giản, một lớp lót, di động, có thể mở rộng để thêm các ràng buộc hơn. Sau đó, tôi đã cố gắng nhìn nó với một góc nhìn khác, đó là khi tôi nhận ra mình có thể làm mà không cần tùy chọn "một lớp lót" nếu "2 lớp lót" đáp ứng các tiêu chí còn lại của tôi. Cuối cùng, tôi đã tìm ra giải pháp này, tôi thích nó hoạt động trong cả Ubuntu và Mac mà tôi muốn chia sẻ với mọi người:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

Trong lệnh đầu tiên, grep tìm kiếm các số dòng có chứa "foo", cut / head chọn lần xuất hiện đầu tiên và op số học tăng số lần xuất hiện đầu tiên của số 1 vì tôi muốn chèn sau lần xuất hiện. Trong lệnh thứ hai, đó là một chỉnh sửa tệp tại chỗ, "i" để chèn: một ansi-c trích dẫn dòng mới, "bar", sau đó là một dòng mới khác. Kết quả là thêm một dòng mới chứa "thanh" sau dòng "foo". Mỗi trong số 2 lệnh này có thể được mở rộng thành các hoạt động phức tạp hơn và khớp.

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.