How do I add a line of text to the middle of a file using bash?

56,581

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 expression
  • a 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

Share:
56,581

Related videos on Youtube

warnabas
Author by

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, 2022

Comments

  • warnabas
    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
    warnabas almost 13 years
    This 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
    warnabas almost 13 years
    That'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
    brandizzi almost 13 years
    Oh, 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 added s/^\n// to the command so it will remove the newline at the beginning of the content of pattern space.
  • bitmask
    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
    warnabas almost 13 years
    When running this same script on another machine with only one nameserver line it's not inserting anything.
  • brandizzi
    brandizzi almost 13 years
    Probably 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
    Juan almost 11 years
    How 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
    Matthew Herbst about 8 years
    Could this answer cause a problem due to the hold space if the file were massive and couldn't fit in memory?
  • Nolwennig
    Nolwennig over 7 years
    I was inspired by your answer to help sleske : serverfault.com/a/745968/329412
  • MestreLion
    MestreLion almost 7 years
    Great awk solution, maybe the only one that does not copy the entire file to memory...
  • Rana Ghosh
    Rana Ghosh over 2 years
    @MestreLion how do you figure? It reads every line, doesn't it? (I also like this solution)
  • MestreLion
    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
    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