How do I add a line of text to the middle of a file using bash?
Solution 1
Here is a solution using sed:
$ sed -n 'H;${x;s/^\n//;s/nameserver .*$/nameserver 127.0.0.1\n&/;p;}' resolv.conf
# Generated by NetworkManager
domain dhcp.example.com
search dhcp.example.com
nameserver 127.0.0.1
nameserver 10.0.0.1
nameserver 10.0.0.2
nameserver 10.0.0.3
How it works: first, suppress the output of sed with the -n
flag. Then, for each line, we append the line to the hold space, separating them with newlines:
H
When we come to the end of the file (addressed by $
) we move the content of the hold space to the pattern space:
x
If the first line in pattern space is blank we replace it with nothing.
s/^\n//
Then we replace the first line starting with nameserver
by a line containing nameserver 127.0.0.1
, a new line (Your version of sed
may not support \n
, in which case replace the n
with a literal newline) and the original line (represented by &
):
s/nameserver .*$/nameserver 127.0.0.1\n&/
Now we just need to print the results:
p
Solution 2
Assuming you want to insert immediately after the search
line, this is much simpler:
sed -ie '/^search/a nameserver 127.0.0.1' filename
-i
: edit file in place-e
: allows the execution of a script/commands inside sed expressiona mynewtext
: command that tells sed to insert the text mynewtext after matched pattern
Solution 3
awk '/^nameserver/ && !modif { printf("INSERT\n"); modif=1 } {print}'
Solution 4
How about something like:
sed -e ':a;N;$!ba;s/nameserver/nameserver 127.0.0.1\nnameserver/' /etc/resolv.conf
(similar to this: sed: Find pattern over two lines, not replace after that pattern)
Solution 5
Here's a Perl solution:
perl -lne 'if (not $f and /^nameserver/){ print "nameserver 127.0.0.1"; $f=1 }; print' resolv.conf
-n
loop around every line of the input file, do not automatically print every line-l
removes newlines before processing, and adds them back in afterwards-e
execute the perl code
$f
is used as a flag to indicate that the nameserver string has already been found
Related videos on Youtube
warnabas
Passionate PHP developer Penguin Linux junkie Desktop computer Avid PC gamer Coffee aficionado Woman and man holding hands Dedicated husband Family (man, girl, boy) Proud father of two
Updated on July 09, 2022Comments
-
warnabas almost 2 years
I'm trying to add a line of text to the middle of a text file in a bash script. Specifically I'm trying add a nameserver to my /etc/resolv.conf file. As it stands, resolv.conf looks like this:
# Generated by NetworkManager domain dhcp.example.com search dhcp.example.com nameserver 10.0.0.1 nameserver 10.0.0.2 nameserver 10.0.0.3
My goal is to add
nameserver 127.0.0.1
above all other nameserver lines, but below any text above that. In the end I want to my resolve.conf file to look like this:# Generated by NetworkManager domain dhcp.example.com search dhcp.example.com nameserver 127.0.0.1 nameserver 10.0.0.1 nameserver 10.0.0.2 nameserver 10.0.0.3
How is this possible via a bash script? Is this something sed or awk can do? Or would creative greping to recreate the file be my best move?
-
warnabas almost 13 yearsThis is great. Only thing weird is it adds a blank line at the top of the new file. Not a big deal, but it's causing an OCD moment.
-
warnabas almost 13 yearsThat's the thing... that search line may or may not be there. The nameserver lines should always be there, and I want the new line inserted immediately before them regardless of the rest of the contents of the file.
-
brandizzi almost 13 yearsOh, yes, you are right! The command
sed -n 'H;${x;s/^\n//;s/nameserver .*\n/nameserver 127.0.0.1\n&/;p;}' resolv.conf
, however, solves this problem. I addeds/^\n//
to the command so it will remove the newline at the beginning of the content of pattern space. -
bitmask almost 13 years@PHLAK, the solution offered by Jim is good. I would modify it, though. Simply add a special comment to mark the line where new stuff is supposed to be entered. E.g. a line like
#<<INSERT>>
or something. Then, instead of/^search/
you can look for/^#<<INSERT>>$/
, making it far more unlikely to match the wrong line. -
warnabas almost 13 yearsWhen running this same script on another machine with only one nameserver line it's not inserting anything.
-
brandizzi almost 13 yearsProbably because there is no newline at the end of the line. It is easy to solve, too:
sed -n 'H;${x;s/^\n//;s/nameserver .*$/nameserver 127.0.0.1\n&/;p;}' resolv.conf
Now we replace from the first nameserver until the end of the file - I replaced the\n
in/nameserver .*\n/
by the marker of end of line$
. Again, not that the\n
in/nameserver 127.0.0.1\n&/
may have to be replaced by a `` followed by an actual newline in some sed versions. -
Juan almost 11 yearsHow would you modify this (if possible) to use a single line from another file (assume that this file has only one line), instead of
nameserver 127.0.0.1
? -
Matthew Herbst about 8 yearsCould this answer cause a problem due to the hold space if the file were massive and couldn't fit in memory?
-
Nolwennig over 7 yearsI was inspired by your answer to help sleske : serverfault.com/a/745968/329412
-
MestreLion almost 7 yearsGreat
awk
solution, maybe the only one that does not copy the entire file to memory... -
Rana Ghosh over 2 years@MestreLion how do you figure? It reads every line, doesn't it? (I also like this solution)
-
MestreLion over 2 years@DanielKaplan: it does read and print every line, but one at a time, so the memory footprint does not depend on file size. Other solutions might accumulate until/if/after the insertion point is found, which is not desirable for arbitrarily large files.
-
CervEd over 2 years@Pom12 what do you mean by
-e : allows the execution of a script/commands inside sed expression
. Correct me if I'm wrong but I believe the-e
flag does nothing. AFAIK it's used to specify multiple different sed commands and that's it. This is just 1