Làm thế nào để chèn một dòng mới trước một mẫu?


138

Làm thế nào để chèn một dòng mới trước một mẫu trong một dòng?

Ví dụ, điều này sẽ chèn một dòng mới đằng sau mẫu regex.

sed 's/regex/&\n/g'

Làm thế nào tôi có thể làm tương tự nhưng ở phía trước của mô hình?

Cho tệp đầu vào mẫu này, mẫu để khớp là số điện thoại.

some text (012)345-6789

Nên trở thành

some text
(012)345-6789


1
@NilsvonBarth, tại sao một câu hỏi đơn giản lại là một câu hỏi tồi?
Josh

Câu trả lời:


177

Điều này hoạt động trong bashzsh, được thử nghiệm trên Linux và OS X:

sed 's/regexp/\'$'\n/g'

Nói chung, $theo sau là một chuỗi ký tự trong các trích dẫn đơn bashthực hiện thay thế dấu gạch chéo ngược kiểu C, ví dụ: $'\t'được dịch sang một tab bằng chữ. Thêm vào đó, sed muốn dòng chữ mới của bạn được thoát với dấu gạch chéo ngược, do đó \trước đó $. Và cuối cùng, đồng đô la tự nó không nên được trích dẫn để nó được giải thích bởi vỏ, do đó chúng tôi đóng dấu ngoặc kép trước $và sau đó mở lại.

Chỉnh sửa : Như được đề xuất trong các nhận xét của @ mkuity0, điều này cũng hoạt động:

sed $'s/regexp/\\\n/g'

Điều xảy ra ở đây là: toàn bộ lệnh sed bây giờ là một chuỗi kiểu C, có nghĩa là dấu gạch chéo ngược mà sed yêu cầu phải được đặt trước dòng chữ mới nên được thoát bằng dấu gạch chéo ngược khác. Mặc dù dễ đọc hơn, nhưng trong trường hợp này, bạn sẽ không thể thực hiện thay thế chuỗi vỏ (mà không làm cho nó xấu đi một lần nữa.)


7
Điều này mang lại cho tôi "dòng mới không thoát trong mẫu thay thế" trên OSX.
Matt Gibson

@Matt Gibson điều đó rất lạ bởi vì "dòng mới không thoát" chỉ được đưa ra khi bạn có một dòng mới thực sự mà không có dấu gạch chéo ngược trong mẫu thay thế. Mã của tôi ở trên hoạt động, trên thực tế, trong một số shell khác, ví dụ zsh, ksh.
mojuba

3
@Matt Gibson ... hoặc nếu bạn quên dấu gạch chéo ngược trước '$' \ n trong mã của tôi.
mojuba

7
Như đã viết, các biểu thức này thay thế hoàn toàn regex bằng một dòng mới, thay vì chèn một dòng mới vào giữa một dòng hiện có như yêu cầu. Đây là cách tôi sử dụng một hình thức sửa đổi của câu trả lời này để chèn một dòng mới giữa hai mẫu phù hợp : sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Lưu ý hai dấu ngoặc đơn sau \ n. Phần đầu tiên đóng phần " $" để phần còn lại của dòng không bị ảnh hưởng bởi nó. Không có những trích dẫn đó, \ 2 đã bị bỏ qua.
David Ravetti

12
Một lựa chọn khác là sử dụng một đơn chuỗi ANSI C-trích dẫn : sed $'s/regexp/\\\n/g', trong đó cải thiện khả năng đọc - caveat duy nhất là bạn thì cần phải tăng gấp đôi tất cả nghĩa đen \ chars.
mkuity0

43

Một số câu trả lời khác không hoạt động cho phiên bản sed của tôi. Chuyển đổi vị trí &\nđã làm việc.

sed 's/regexp/\n&/g' 

Chỉnh sửa: Điều này dường như không hoạt động trên OS X, trừ khi bạn cài đặt gnu-sed.


9
Tôi không chắc điều này hoạt động trong tất cả các phiên bản của sed. Tôi đã thử điều này trên máy Mac của mình và \ n chỉ nhận được đầu ra là 'n'
Todd Gamblin

3
Dành 15 phút trên máy Mac tại nơi làm việc của tôi, trước khi đọc câu trả lời của bạn. Đi Apple!
Rick77 7/07/2015

1
Đối với những người sử dụng homebrew: brew install gnu-sedtheo saugsed 's/regexp/\n&/g'
aaaaaa

1
... tiếp theoecho 'alias sed=gsed' >> ~/.bashrc
ProSenseo

36

Trong sed, bạn không thể thêm dòng mới vào luồng đầu ra một cách dễ dàng. Bạn cần sử dụng một dòng tiếp tục, điều này thật bất tiện, nhưng nó hoạt động:

$ sed 's/regexp/\
&/'

Thí dụ:

$ echo foo | sed 's/.*/\
&/'

foo

Xem ở đây để biết chi tiết. Nếu bạn muốn một cái gì đó bớt khó xử một chút, bạn có thể thử sử dụng perl -pevới các nhóm khớp thay vì sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 đề cập đến nhóm khớp đầu tiên trong biểu thức chính quy, trong đó các nhóm nằm trong ngoặc đơn.


Tại sao bạn nói bạn không thể thêm dòng mới? Bạn chỉ có thể thực hiện sed 's / regapi / & \ n / g' Đó là thế
Andres

2
Đây là điều ít tìm kiếm nhất mà bạn có thể làm trên máy Mac để chèn dòng mới (\ n không hoạt động trên máy mac)
Pylinux 4/2/2015

Phiên bản perl có thể được sửa đổi để thực hiện chỉnh sửa tại chỗperl -pi -e 's/(.*)/\n$1/' foo
Eponymous

2
@Andres: (Hầu hết) Các triển khai Sed chỉ dành cho tính năng POSIX như phiên bản BSD đi kèm với OS X không hỗ trợ các chuỗi thoát ký tự điều khiển trong phần thay thế của một slệnh gọi chức năng (không giống như triển khai GNU Sed) . Câu trả lời trên hoạt động với cả hai triển khai; để biết tổng quan về tất cả sự khác biệt, xem tại đây .
mkuity0

29

Trên máy mac của tôi, các mục sau đây chèn một 'n' thay vì dòng mới:

sed 's/regexp/\n&/g'

Điều này thay thế bằng dòng mới:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Tôi đang thực hiện chỉnh sửa nội tuyến sed -i '' -e ...và gặp vấn đề với ^Mdấu mũ (M (ctrl + m) được ghi vào tệp. Tôi đã kết thúc bằng cách sử dụng perl với cùng thông số.
Steve Tauber

2
Xin lưu ý rằng thực tế là mã thứ hai chèn mã dòng mới đặc biệt là LF CR (ngược lại với MS-DOS CR LF)! Cả hai hệ điều hành giống như Unix và Mac OS X đều chỉ sử dụng LF ( \n).
pabouk

Một cái gì đó khác trong biểu hiện quyến rũ của tôi đã gây ra rất nhiều bất hạnh (mặc dù nó hoạt động tốt mà không có echo...và dòng mới) mà tôi chỉ làm điều này trong vim.
Ahmed Fasih

1
Hoặc đơn giản là: sed "s/regexp/`echo`/g"- điều này sẽ tạo ra một LF duy nhất thay vì LF-CR
mojuba

2
@mojuba: Không: `echo`sẽ dẫn đến chuỗi trống , bởi vì các lệnh thay thế luôn luôn cắt xén tất cả các dòng mới. Không có cách nào để sử dụng thay thế lệnh để chèn trực tiếp một dòng mới (và chèn \n\r- tức là thêm CR - là một ý tưởng tồi tệ).
mkuity0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 hoạt động hoàn hảo và khá ổn định / dễ nhớ
gMale

2
Câu trả lời này thực sự là một giải pháp sed chứ không phải là một giải pháp bash . Bất cứ điều gì sử dụng các cấu trúc như $'\n'là dựa vào shell để tạo ra dòng mới. Các giải pháp như vậy có thể không được xách tay. Đây là một. Tất nhiên, đó cũng là một bản sao của ví dụ thứ hai trong câu trả lời của tgamblin từ năm 2009.
ghoti

10

Trong trường hợp này, tôi không sử dụng sed. Tôi dùng tr.

cat Somefile |tr ',' '\012' 

Điều này có dấu phẩy và thay thế nó bằng trở lại vận chuyển.


1
Tôi thấy điều này cũng hoạt động: cat Somefile | tr ',' '\n'YMMV
LS

9

Bạn có thể sử dụng perl one-liners giống như bạn làm với sed, với lợi thế là hỗ trợ biểu thức chính quy perl đầy đủ (mạnh hơn nhiều so với những gì bạn nhận được với sed). Cũng có rất ít biến thể trên các nền tảng * nix - perl thường là perl. Vì vậy, bạn có thể ngừng lo lắng về cách làm cho phiên bản sed của hệ thống cụ thể của bạn làm những gì bạn muốn.

Trong trường hợp này, bạn có thể làm

perl -pe 's/(regex)/\n$1/'

-pe đặt perl vào một vòng lặp "thực thi và in", giống như chế độ hoạt động bình thường của sed.

' trích dẫn mọi thứ khác để vỏ không can thiệp

()xung quanh regex là một toán tử nhóm. $1ở phía bên phải của sự thay thế in ra bất cứ điều gì phù hợp bên trong các parens này.

Cuối cùng, \nlà một dòng mới.

Bất kể bạn đang sử dụng dấu ngoặc đơn như là một toán tử nhóm, bạn phải thoát khỏi bất kỳ dấu ngoặc đơn nào bạn đang cố gắng khớp. Vì vậy, một biểu thức chính quy để khớp với mẫu bạn liệt kê ở trên sẽ giống như

\(\d\d\d\)\d\d\d-\d\d\d\d

\(hoặc \)phù hợp với một paren nghĩa đen, và \dphù hợp với một chữ số.

Tốt hơn:

\(\d{3}\)\d{3}-\d{4}

Tôi tưởng tượng bạn có thể tìm ra những con số trong niềng răng đang làm gì.

Ngoài ra, bạn có thể sử dụng các dấu phân cách khác hơn / cho biểu thức chính quy của bạn. Vì vậy, nếu bạn cần khớp / bạn sẽ không cần phải thoát nó. Một trong những điều dưới đây tương đương với regex ở đầu câu trả lời của tôi. Về lý thuyết, bạn có thể thay thế bất kỳ ký tự nào cho tiêu chuẩn / 's.

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Một vài suy nghĩ cuối cùng.

sử dụng -nethay vì -pehành động tương tự, nhưng không tự động in ở cuối. Nó có thể hữu ích nếu bạn muốn tự in. Ví dụ: đây là một grep-alike ( m/foobar/là một trận đấu regex):

perl -ne 'if (m/foobar/) {print}'

Nếu bạn đang tìm cách xử lý các dòng mới rắc rối và bạn muốn nó được xử lý một cách kỳ diệu cho bạn, hãy thêm -l. Mặc dù không hữu ích cho OP, người đã làm việc với các dòng mới.

Tiền thưởng - nếu bạn đã cài đặt gói pcre, nó đi kèm pcregrep, sử dụng các biểu thức tương thích hoàn toàn với perl.


4

Hmm, mới thoát khỏi dòng mới dường như hoạt động trong các phiên bản gần đây hơn của sed(Tôi có GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Như đã đề cập, điều này hoạt động với các phiên bản khác nhau của GNU sed, nhưng không phải là sed đi kèm với macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

làm việc tốt trên El Captitan với ()sự hỗ trợ


Điều này hoạt động rất tốt và bạn thậm chí còn đưa ra một lệnh đầy đủ để kiểm tra và ngoại suy từ để chuyên môn hóa cho mục đích riêng của mình. Công việc tốt!
jxramos

3

Để chèn một dòng mới vào luồng đầu ra trên Linux, tôi đã sử dụng:

sed -i "s/def/abc\\\ndef/" file1

Trong trường hợp file1là:

def

Trước khi thay thế tại chỗ sed, và:

abc
def

Sau khi thay thế tại chỗ sed. Xin lưu ý việc sử dụng \\\n. Nếu các mẫu có một "bên trong nó, thoát bằng cách sử dụng \".


Đối với tôi mã ở trên không hoạt động. sedchèn \nthay vì LF vì nó nhận được \\ntham số từ trình bao. --- Mã này hoạt động : sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(Bản phát hành CentOS 6.4 / Ubuntu 12.04.3).
pabouk

2

trong sed, bạn có thể tham chiếu các nhóm trong mẫu của mình bằng "\ 1", "\ 2", .... vì vậy nếu mẫu bạn đang tìm là "THỰC HIỆN" và bạn muốn chèn "TRƯỚC" phía trước mẫu đó , bạn có thể sử dụng, sans thoát

sed 's/(PATTERN)/BEFORE\1/g'

I E

  sed 's/\(PATTERN\)/BEFORE\1/g'

Chỉ cần làm: testfile nội dung = "ABC ABC ABC". Ran "sed 's / \ (ABC \) / \ n \ 1 / g' testfile, đã nhận được các dòng mới. Thử nghiệm với các lối thoát, cố gắng thêm 1 điều vào mẫu của bạn, ví dụ: đảm bảo bạn khớp với mẫu, sau đó kiểm tra kết hợp nhóm, sau đó thêm kiểm tra dòng mới.
Steve B.

Tôi vừa thử chính xác điều đó và nhận được "nABC nABC nABC '. Bạn có đang sử dụng một số phiên bản khác của sed không?
Todd Gamblin

thoát vỏ có lẽ đang cản trở những nỗ lực của tgamblin. đặt các đối số sed đầy đủ trong các trích dẫn đơn như Steve B đã làm nên sửa điều đó. Mặc dù có thể các phiên bản khác nhau của sed không hiểu \ n cho dòng mới.
Dan Pritts

2

Bạn cũng có thể làm điều này với awk, sử dụng -vđể cung cấp mẫu:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

Điều này kiểm tra nếu một dòng chứa một mẫu nhất định. Nếu vậy, nó sẽ thêm một dòng mới vào đầu của nó.

Xem một ví dụ cơ bản:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Lưu ý rằng nó sẽ ảnh hưởng đến tất cả các mẫu trong một dòng:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead

1
1 làm gì trong này?
whatahitson

1
@whatahitson 1được sử dụng trong Awk như một cách viết tắt {print $0}. Lý do là bất kỳ điều kiện nào được đánh giá là True sẽ kích hoạt hành động mặc định của Awk, bao gồm in bản ghi hiện tại.
fedorqui 'SO ngừng làm hại'

1

Cái này hoạt động trong MAC đối với tôi

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Dono cho dù nó hoàn hảo ...


1

Sau khi đọc tất cả các câu trả lời cho câu hỏi này, tôi vẫn mất nhiều nỗ lực để có được cú pháp chính xác cho tập lệnh ví dụ sau:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

Kịch bản nối thêm một dòng mới \ntheo sau là một dòng văn bản khác vào cuối tệp bằng một sedlệnh duy nhất .


1
sed -e 's/regexp/\0\n/g'

\ 0 là null, vì vậy biểu thức của bạn được thay thế bằng null (không có gì) và sau đó ...
\ n là dòng mới

Trên một số hương vị của Unix không hoạt động, nhưng tôi nghĩ đó là giải pháp cho vấn đề của bạn.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

Trong vi trên Red Hat, tôi đã có thể chèn trả về vận chuyển chỉ bằng ký tự \ r. Tôi tin rằng điều này trong nội bộ thực thi 'ex' thay vì 'sed', nhưng nó tương tự, và vi có thể là một cách khác để thực hiện các chỉnh sửa hàng loạt như các bản vá mã. Ví dụ. Tôi đang bao quanh một cụm từ tìm kiếm với một câu lệnh if khăng khăng đòi trả lại xe sau khi niềng răng:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Lưu ý rằng tôi cũng đã chèn nó vào một số tab để làm cho mọi thứ phù hợp hơn.

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.