ChatGPT解决这个技术问题 Extra ChatGPT

How to insert a newline in front of a pattern?

How to insert a newline before a pattern within a line?

For example, this will insert a newline behind the regex pattern.

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

How can I do the same but in front of the pattern?

Given this sample input file, the pattern to match on is the phone number.

some text (012)345-6789

Should become

some text
(012)345-6789
Just repeating the answer to How do I insert a newline/linebreak after a line using sed here: sed '/regex/G'
@NilsvonBarth, why is a straightforward question a bad question?

m
mojuba

This works in bash and zsh, tested on Linux and OS X:

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

In general, for $ followed by a string literal in single quotes bash performs C-style backslash substitution, e.g. $'\t' is translated to a literal tab. Plus, sed wants your newline literal to be escaped with a backslash, hence the \ before $. And finally, the dollar sign itself shouldn't be quoted so that it's interpreted by the shell, therefore we close the quote before the $ and then open it again.

Edit: As suggested in the comments by @mklement0, this works as well:

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

What happens here is: the entire sed command is now a C-style string, which means the backslash that sed requires to be placed before the new line literal should now be escaped with another backslash. Though more readable, in this case you won't be able to do shell string substitutions (without making it ugly again.)


This gives me "unescaped newline inside substitute pattern" on OSX.
@Matt Gibson that's very strange because "unescaped newline" is given only when you have a real newline without a backslash within the substitution pattern. My code above works, in fact, in some other shells too, e.g. zsh, ksh.
@Matt Gibson ... or if you forget the backslash before '$'\n in my code.
As written, these expressions replace the regex completely with a newline, rather than inserting a newline in the middle of an existing line as requested. Here's how I used a modified form of this answer to insert a newline between two matched patterns: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Note the two single quotes after the \n. The first closes off the "$" section so that the remainder of the line is not affected by it. Without those quotes, the \2 was ignored.
Another option is to use a single ANSI C-quoted string: sed $'s/regexp/\\\n/g', which improves readability - the only caveat is that you then need to double all literal \ chars.
D
Dennis

Some of the other answers didn't work for my version of sed. Switching the position of & and \n did work.

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

Edit: This doesn't seem to work on OS X, unless you install gnu-sed.


I'm not sure this works in all versions of sed. I tried this on my Mac and the \n just gets output as 'n'
Spent 15 minutes on the mac at my work, before reading your answer. Go Apple!
For those using homebrew: brew install gnu-sed followed by gsed 's/regexp/\n&/g'
...followed by echo 'alias sed=gsed' >> ~/.bashrc
T
Todd Gamblin

In sed, you can't add newlines in the output stream easily. You need to use a continuation line, which is awkward, but it works:

$ sed 's/regexp/\
&/'

Example:

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

foo

See here for details. If you want something slightly less awkward you could try using perl -pe with match groups instead of sed:

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

foo

$1 refers to the first matched group in the regular expression, where groups are in parentheses.


Why do you say you can't add newlines? You can just do sed 's/regexp/&\n/g' That's it
This is the least hacky looking thing you can do on a Mac to insert newline (\n doesn't work on a mac)
The perl version can be modified to do in place editing perl -pi -e 's/(.*)/\n$1/' foo
@Andres: (Mostly) POSIX-features-only Sed implementations such as the BSD version that also comes with OS X do not support control-character escape sequences in the substitution part of an s function call (unlike the GNU Sed implementation, which does). The above answer works with both implementations; for an overview of all differences, see here.
R
Roar Skullestad

On my mac, the following inserts a single 'n' instead of newline:

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

This replaces with newline:

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

I was doing inline edit sed -i '' -e ... and was having problems with a ^M caret M (ctrl+m) getting written to the file. I ended up using perl with the same params.
Please be aware of the fact that the second code inserts a special newline code LF CR (reverse of the MS-DOS CR LF)! Both Unix-like OSes and Mac OS X use just LF (\n).
Something else in my sed expression was causing so much unhappiness (despite it working fine without the echo... and newline) that I just did this in vim.
Or simply: sed "s/regexp/`echo`/g" - this will produce a single LF instead of LF-CR
@mojuba: No: `echo` will result in the empty string, because command substitutions invariably trim all trailing newlines. There is no way to use a command substitution to directly insert a single newline (and inserting \n\r - i.e., an additional CR - is a terrible idea).
g
gMale
echo one,two,three | sed 's/,/\
/g'

+1 worked perfectly and pretty stright-forward/easy-to-remember
This answer is actually a sed solution rather than a bash solution. Anything that uses constructs like $'\n' is relying on the shell to generate the newline. Such solutions may not be portable. This one is. Of course, it's also a duplicate of the second example in tgamblin's answer from 2009.
This is the only version that worked for me in Solaris.
D
Dan Pritts

You can use perl one-liners much like you do with sed, with the advantage of full perl regular expression support (which is much more powerful than what you get with sed). There is also very little variation across *nix platforms - perl is generally perl. So you can stop worrying about how to make your particular system's version of sed do what you want.

In this case, you can do

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

-pe puts perl into a "execute and print" loop, much like sed's normal mode of operation.

' quotes everything else so the shell won't interfere

() surrounding the regex is a grouping operator. $1 on the right side of the substitution prints out whatever was matched inside these parens.

Finally, \n is a newline.

Regardless of whether you are using parentheses as a grouping operator, you have to escape any parentheses you are trying to match. So a regex to match the pattern you list above would be something like

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

\( or \) matches a literal paren, and \d matches a digit.

Better:

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

I imagine you can figure out what the numbers in braces are doing.

Additionally, you can use delimiters other than / for your regex. So if you need to match / you won't need to escape it. Either of the below is equivalent to the regex at the beginning of my answer. In theory you can substitute any character for the standard /'s.

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

A couple final thoughts.

using -ne instead of -pe acts similarly, but doesn't automatically print at the end. It can be handy if you want to print on your own. E.g., here's a grep-alike (m/foobar/ is a regex match):

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

If you are finding dealing with newlines troublesome, and you want it to be magically handled for you, add -l. Not useful for the OP, who was working with newlines, though.

Bonus tip - if you have the pcre package installed, it comes with pcregrep, which uses full perl-compatible regexes.


u
user1612632

In this case, I do not use sed. I use tr.

cat Somefile |tr ',' '\012' 

This takes the comma and replaces it with the carriage return.


I found this also works: cat Somefile | tr ',' '\n' YMMV
g
gatoatigrado

Hmm, just escaped newlines seem to work in more recent versions of sed (I have GNU sed 4.2.1),

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

As mentioned, this works with various versions of GNU sed, but not the sed included with macOS.
Q
Quanlong
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

worked fine on El Captitan with () support


This worked great and you even give a full command to test and extrapolate from to specialize for one's own purpose. Nice job!
S
Sicco

To insert a newline to output stream on Linux, I used:

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

Where file1 was:

def

Before the sed in-place replacement, and:

abc
def

After the sed in-place replacement. Please note the use of \\\n. If the patterns have a " inside it, escape using \".


For me the code above does not work. sed inserts \n instead of LF because it gets \\n in the parameter from the shell. --- This code works: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1) (CentOS release 6.4 / Ubuntu 12.04.3).
v
vijayraj34

In my case the below method works.

sed -i 's/playstation/PS4/' input.txt

Can be written as:

sed -i 's/playstation/PS4\nplaystation/' input.txt

PS4 playstation

Consider using \\n while using it in a string literal.

sed : is stream editor

-i : Allows to edit the source file

+: Is delimiter.

I hope the above information works for you 😃.


S
Steve B.

in sed you can reference groups in your pattern with "\1", "\2", .... so if the pattern you're looking for is "PATTERN", and you want to insert "BEFORE" in front of it, you can use, sans escaping

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

i.e.

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

Just did: testfile contents="ABC ABC ABC". Ran "sed 's/\(ABC\)/\n\1/g' testfile, got the newlines. Experiment with the escapes, try to add 1 thing at a time to your pattern, e.g. make sure you're matching the pattern, then check the group matching, then add the newline checking.
I just tried exactly that and got "nABC nABC nABC'. Are you using some other version of sed?
shell escaping is probably getting in the way of tgamblin's attempts. putting the full sed arguments in single quotes like Steve B did should fix that. Possible though that different versions of sed don't understand the \n for newline.
J
Jonathan Leffler

You can also do this with awk, using -v to provide the pattern:

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

This checks if a line contains a given pattern. If so, it appends a new line to the beginning of it.

See a basic example:

$ 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!

Note it will affect to all patterns in a line:

$ 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

what does the 1 do in this?
@whatahitson 1 is used in Awk as a shorthand to {print $0}. The reason is that any condition that evaluates to True triggers Awk's default action, which consists in printing the current record.
s
slm
sed -e 's/regexp/\0\n/g'

\0 is the null, so your expression is replaced with null (nothing) and then... \n is the new line

On some flavors of Unix doesn't work, but I think it's the solution to your problem.

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

l
laalto

This works in MAC for me

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

Dono whether its perfect one...


X
Xavier

After reading all the answers to this question, it still took me many attempts to get the correct syntax to the following example script:

#!/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

The script appends a newline \n followed by another line of text to the end of a file using a single sed command.


R
Robert Casey

In vi on Red Hat, I was able to insert carriage returns using just the \r character. I believe this internally executes 'ex' instead of 'sed', but it's similar, and vi can be another way to do bulk edits such as code patches. For example. I am surrounding a search term with an if statement that insists on carriage returns after the braces:

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

Note that I also had it insert some tabs to make things align better.