Adding content from file

The previous chapter discussed how to use a, c and i commands to append, change or insert the given string for matching addresses. Any \ in the string argument is treated according to sed escape sequence rules and literal newline characters are not allowed.

The r and R commands use file content as the source string, which is always treated literally and can contain newline characters. Thus, these two commands provide a robust way to add text literally. However, these commands provide only the append functionality. Other sed features will be used to construct change and insert variations.

info The example_files directory has all the files used in the examples.

Append entire file using r

The r command accepts a filename argument and the entire content of the given file is added after each of the matching lines.

$ cat ip.txt     * sky     * apple $ cat fav_color.txt deep red yellow reddish brown  # space between r and filename is optional # add entire content of 'ip.txt' after each line containing 'red' $ sed '/red/r ip.txt' fav_color.txt deep red     * sky     * apple yellow reddish     * sky     * apple brown 

warning If the given filename doesn't exist, sed will silently ignore it and proceed as if the file was empty. Exit status will be 0 unless something else goes wrong with the sed command used.

To use a command output as the input file source, you can use /dev/stdin as the filename. However, once the content of /dev/stdin is exhausted, you won't get any more data. So, using /dev/stdin is not always functionally equivalent to providing a filename. As an example, compare the output for cat ip.txt | sed '/red/r /dev/stdin' fav_color.txt to sed '/red/r ip.txt' fav_color.txt. So, if you have multiple matches, save the command output to a file first and then use that filename with r instead of /dev/stdin.

$ text='good\tone\nfood\tpun' $ echo "$text" | sed '1r /dev/stdin' ip.txt     * sky good\tone\nfood\tpun     * apple  # example for adding multiline command output $ seq 2 | sed '2r /dev/stdin' ip.txt     * sky     * apple 1 2  # note that newline won't be added to the file content being read $ printf '123' | sed '1r /dev/stdin' ip.txt     * sky 123    * apple 

info You can use 0 as the address to insert file contents before the first line. This feature was added in GNU sed 4.9 to the r command.

$ sed '0r ip.txt' greeting.txt     * sky     * apple Hi there Have a nice day 

Here are some examples to emulate the change command functionality. As seen in the previous chapter, use the -e option or literal newlines when multiple commands are needed. See also unix.stackexchange: Various ways to replace line M in file1 with line N in file2.

# replace only the 2nd line; order is important, first 'r' and then 'd' # note the use of command grouping to avoid repeating the address $ items='    * blue\n    * green\n' $ printf '%b' "$items" | sed -e '2 {r /dev/stdin' -e 'd}' ip.txt     * sky     * blue     * green  # replace a range of lines # command grouping will add file contents for each matching line # so, use 'r' only for one of the addresses and then delete the range # // here avoids address duplication # same as: sed -e '/^red/r ip.txt' -e '/yellow/,//d' fav_color.txt $ sed -e '/yellow/r ip.txt' -e '//,/^red/d' fav_color.txt deep red     * sky     * apple brown 

Quoting from the manual:

The empty regular expression // repeats the last regular expression match (the same holds if the empty regular expression is passed to the s command). Note that modifiers to regular expressions are evaluated when the regular expression is compiled, thus it is invalid to specify them together with the empty regular expression

Emulating insert command functionality with the r command requires advanced usage of sed, which is beyond the scope of this book. See unix.stackexchange: insert file contents before matching line for examples. Instead, the next section will show how to use the e flag for this purpose.

Insert file using e and cat

The manual has this handy note for the e flag:

Note that, unlike the r command, the output of the command will be printed immediately; the r command instead delays the output to the end of the current cycle.

This makes the e flag the easiest way to insert file content before the matching lines. Similar to the r command, the output of an external command is inserted literally. But one difference from the r command is that if the filename passed to the external cat command doesn't exist, then you will see its error message inserted.

$ sed '/red/e cat ip.txt' fav_color.txt     * sky     * apple deep red yellow     * sky     * apple reddish brown 

Append line by line using R

The R command is very similar to the r command. But instead of reading the entire file content, R will read one line at a time from the source file when the address matches. If the entire file has already been consumed and another match is found, sed will proceed as if the next line to read was empty.

$ sed '/red/R ip.txt' fav_color.txt deep red     * sky yellow reddish     * apple brown  # interleave contents of two files $ seq 4 | sed 'R /dev/stdin' fav_color.txt deep red 1 yellow 2 reddish 3 brown 4 

info See also stackoverflow: Replace first few lines with first few lines from other file.

Cheatsheet and summary

NoteDescription
r filenameentire file content is added after each matching line
e cat filenameentire file content is added before each matching line
R filenameadd one line at a time after each matching line
space between the command and the filename is optional
/dev/stdin as the filename will use stdin data
content is added literally, unlike the a, c and i commands

This chapter showed robust solutions for adding text literally from a file or command output. These are particularly useful for templating solutions where a line containing a keyword gets replaced with text from elsewhere. In the next chapter, you'll learn how to implement control structures using branch commands.

Exercises

info The exercises directory has all the files used in this section.

1) For the input file addr.txt, replace from the third to fifth lines with the second to fourth lines from para.txt.

$ sed ##### add your solution here Hello World How are you Start working on that project you always wanted to, do not let it end You are funny 

2) Add one line from hex.txt after every two lines of copyright.txt.

$ sed ##### add your solution here bla bla 2015 bla blah 2018 blah start address: 0xA0, func1 address: 0xA0 bla bla bla copyright: 2019 end address: 0xFF, func2 address: 0xB0 

3) For every line of the input file hex.txt, insert --- before the line and append one line from replace.txt as shown below.

$ sed ##### add your solution here --- start address: 0xA0, func1 address: 0xA0 0xA0 0x5000 --- end address: 0xFF, func2 address: 0xB0 0xB0 0x6000 

4) Insert the contents of hex.txt before a line matching 0x6000 of the input file replace.txt.

$ sed ##### add your solution here 0xA0 0x5000 start address: 0xA0, func1 address: 0xA0 end address: 0xFF, func2 address: 0xB0 0xB0 0x6000 0xFF 0x7000 

5) For the input file addr.txt, replace lines containing are with contents of hex.txt.

$ sed ##### add your solution here Hello World start address: 0xA0, func1 address: 0xA0 end address: 0xFF, func2 address: 0xB0 This game is good Today is sunny 12345 start address: 0xA0, func1 address: 0xA0 end address: 0xFF, func2 address: 0xB0 

6) The two commands shown below are equivalent. True or False?

sed '/are/r pets.txt' addr.txt  cat pets.txt | sed '/are/r /dev/stdin' addr.txt 

7) What do the commands sed '0r file1' file2 and sed '0R file1' file2 do?