Operating in a pattern range except for the patterns
You may remember that I mentioned you can do a substitute on a pattern range, like changing "old" to "new" between a begin/end pattern:
#!/bin/sh
sed '
/begin/,/end/ s/old/new/
'
Another way to write this is to use the curly braces for grouping:
#!/bin/sh
sed '
/begin/,/end/ {
s/old/new/
}
'
I think this makes the code clearer to understand, and easier to modify, as you will see below.
If you did not want to make any changes where the word "begin" occurred, you could simple add a new condition to skip over that line:
#!/bin/sh
sed '
/begin/,/end/ {
/begin/n # skip over the line that has "begin" on it
s/old/new/
}
'
However, skipping over the line that has "end" is trickier. If you use the same method you used for "begin" then the sed engine will not see the "end" to stop the range - it skips over that as well. The solution is to do a substitute on all lines that don't have the "end" by using
#!/bin/sh
sed '
/begin/,/end/ {
/begin/n # skip over the line that has "begin" on it
/end/ !{
s/old/new/
}
}
'
Writing a file with the 'w' command
You may remember that the substitute command can write to a file. Here again is the example that will only write lines that start with an even number (and followed by a space):
sed -n 's/^[0-9]*[02468] /&/w even' <file
I used the "&" in the replacement part of the substitution command so that the line would not be changed. A simpler example is to use the "w" command, which has the same syntax as the "w" flag in the substitute command:
sed -n '/^[0-9]*[02468]/ w even' <file
Remember - only one space must follow the command. Anything else will be considered part of the file name. The "w" command also has the same limitation as the "w" flag: only 10 files can be opened in sed.
Reading in a file with the 'r' command
There is also a command for reading files. The command
sed '$r end' <in>out
will append the file "end" at the end of the file (address "$)." The following will insert a file after the line with the word "INCLUDE:"
sed '/INCLUDE/ r file' <in >out
You can use the curly braces to delete the line having the "INCLUDE" command on it:
#!/bin/sh
sed '/INCLUDE/ {
r file
d
}'
Click here to get file: sed_include.sh
The order of the delete command "d" and the read file command "r" is important. Change the order and it will not work. There are two subtle actions that prevent this from working. The first is the "r" command writes the file to the output stream. The file is not inserted into the pattern space, and therefore cannot be modified by any command. Therefore the delete command does not affect the data read from the file.
The other subtlety is the "d" command deletes the current data in the pattern space. Once all of the data is deleted, it does make sense that no other action will be attempted. Therefore a "d" command executed in a curly brace also aborts all further actions. As an example, the substitute command below is never executed:
#!/bin/sh
# this example is WRONG
sed -e '1 {
d
s/.*//
}'
Click here to get file: sed_bad_example.sh
The earlier example is a crude version of the C preprocessor program. The file that is included has a predetermined name. It would be nice if sed allowed a variable (e.g "\1)" instead of a fixed file name. Alas, sed doesn't have this ability. You could work around this limitation by creating sed commands on the fly, or by using shell quotes to pass variables into the sed script. Suppose you wanted to create a command that would include a file like cpp, but the filename is an argument to the script. An example of this script is:
% include 'sys/param.h' <file.c >file.c.new
A shell script to do this would be:
#!/bin/sh
# watch out for a '/' in the parameter
# use alternate search delimiter
sed -e '\_#INCLUDE <'"$1"'>_{
r '"$1"'
d
}'
Let me elaborate. If you had a file that contains
Test first file
#INCLUDE <file1>
Test second file
#INCLUDE <file2>
you could use the command
sed_include1.sh file1<input|sed_include1.sh file2
to include the specified files.
Click here to get file: sed_include1.sh
SunOS and the # Comment Command
As we dig deeper into sed, comments will make the commands easier to follow. The older versions of sed only allow one line as a comment, and it must be the first line. SunOS (and GNU's sed) allows more than one comment, and these comments don't have to be first. The last example could be: