Chapter 2. Sed Substitute Command
The most powerful command in the stream editor is substitute. It has such power and so many options that we give it a whole chapter.
6. Sed Substitute Command Syntax
sed '[address-range|pattern-range] s/originalstring/replacement-string/[substitute-flags]' inputfileIn the above sed substitute command syntax:
- address-range or pattern-range is optional. If you don't specify one, sed will execute the substitute command on all lines.
- s – tells Sed to execute the substitute command.
- original-string – this is the string to be searched for in the input file. The original-string can also be a regular expression.
- replacement-string – Sed will replace original-string with this string.
- substitute-flags are optional. More on this in the next section.
$ sed 's/Manager/Director/' employee.txt 101,John Doe,CEO 102,Jason Smith,IT Director 103,Raj Reddy,Sysadmin 104,Anand Ram,Developer 105,Jane Miller,Sales DirectorReplace Manager with Director only on lines that contain the keyword 'Sales':
sed '/Sales/s/Manager/Director/' employee.txt 101,John Doe,CEO 102,Jason Smith,IT Manager 103,Raj Reddy,Sysadmin 104,Anand Ram,Developer 105,Jane Miller,Sales DirectorNote that the use of the address range caused just one change rather than the two shown in the previous example.
7. Global Flag (g flag)
Sed substitute flag g stands for global. By default sed substitute command will replace only the 1st occurrence of the {original-string} on each line. If you want to change all the occurrences of the {original-string} in the line to the {replacement-string}, you should use the global flag g. Replace the 1st occurrence of lower case a with upper case A:$ sed 's/a/A/' employee.txt 101,John Doe,CEO 102,JAson Smith,IT Manager 103,RAj Reddy,Sysadmin 104,AnAnd Ram,Developer 105,JAne Miller,Sales ManagerReplace all occurrences of lower case a with upper case A:
$ sed 's/a/A/g' employee.txt 101,John Doe,CEO 102,JAson Smith,IT MAnAger 103,RAj Reddy,SysAdmin 104,AnAnd RAm,Developer 105,JAne Miller,SAles MAnAgerNote: these examples were applied to the entire file because no address range was specified.
8. Number Flag (1,2,3.. flag)
Use the number flag to specify a specific occurrence of the original-string. Only the n-th instance of original-string will trigger the substitution. Counting starts over on each line, and n can be anything from 1 to 512. For example, /11 will replace only the 11th occurrence of the original-string in a line. The following are few examples. Replace the 2nd occurrence of lower case a to upper case A:$ sed 's/a/A/2' employee.txt 101,John Doe,CEO 102,Jason Smith,IT MAnager 103,Raj Reddy,SysAdmin 104,Anand RAm,Developer 105,Jane Miller,SAles ManagerFor this example, create the following file with three lines:
$ vi substitute-locate.txt locate command is used to locate files locate command uses database to locate files locate command can also use regex for searchingIn the file you just created, change only the 2nd occurrence of locate to find:
$ sed 's/locate/find/2' substitute-locate.txt locate command is used to find files locate command uses database to find files locate command can also use regex for searchingNote: On line 3 in the above example, there is only one "locate" in the original substitute-locate.txt file. So, nothing is changed on line 3.
9. Print Flag (p flag)
The sed substitute flag p stands for print. When the substitution is successful, it prints the changed line. Like most print commands in sed, it is most useful when combined with the -n option to suppress default printing of all lines. Print only the line that was changed by the substitute command:$ sed -n 's/John/Johnny/p' employee.txt 101,Johnny Doe,CEOIn our number flag example, we used /2 to change the 2nd occurrence of "locate" to "find". On line 3 of locate.txt there is no 2nd occurrence and substitution never happened on that line. Adding the p flag to the command we used before will print the two lines that did change. Change the 2nd instance of “locate” to “find” and print the result:
$ sed -n 's/locate/find/2p' substitute-locate.txt locate command is used to find files locate command uses database to find files
10. Write Flag (w flag)
The sed substitute flag w stands for write. When the substitution is successful, it writes the changed line to a file. Most people use the p flag instead, and redirect the output to a file. We include this command for completeness. Write only the line that was changed by the substitute command to output.txt:$ sed -n 's/John/Johnny/w output.txt' employee.txt $ cat output.txt 101,Johnny Doe,CEOJust as we showed for the p command, adding w to our example with the substitute-locate.txt file will send the two lines that were changed to the output file. Change the 2nd instance of “locate” to “find”,write the result to a file, print all lines:
$ sed 's/locate/find/2w output.txt' substitute-locate.txt locate command is used to find files locate command uses database to find files locate command can also use regex for searching $ cat output.txt locate command is used to find files locate command uses database to find files
11. Ignore Case Flag (i flag)
The sed substitute flag i stands for ignore case. You can use the i flag to match the original-string in a case-insensitive manner. This is available only in GNU Sed. In the following example Sed will not replace "John" with "Johnny", as the original-string was given in lower case "john". Replace “john” with Johnny:$ sed 's/john/Johnny/' employee.txt 101,John Doe,CEO 102,Jason Smith,IT Manager 103,Raj Reddy,Sysadmin 104,Anand Ram,Developer 105,Jane Miller,Sales ManagerReplace “john” or “John” with Johnny:
$ sed 's/john/Johnny/i' employee.txt 101,Johnny Doe,CEO 102,Jason Smith,IT Manager 103,Raj Reddy,Sysadmin 104,Anand Ram,Developer 105,Jane Miller,Sales Manager
12. Execute Flag (e flag)
The sed substitute flag e stands for execute. Using the sed e flag, you can execute whatever is available in the pattern space as a shell command, and the output will be returned to the pattern space. This is available only in the GNU sed. The following are few examples. For these examples create the following files.txt that contains a list of filenames with their full path:$ cat files.txt /etc/passwd /etc/groupAdd the text "ls -l " in front of every line in the files.txt and print the output:
$ sed 's/^/ls -l /' files.txt ls -l /etc/passwd ls -l /etc/groupAdd the text "ls -l " in front of every line in the files.txt and execute the output:
$ sed 's/^/ls -l /e' files.txt -rw-r--r-- 1 root root 1547 Oct 27 08:11 /etc/passwd -rw-r--r-- 1 root root 651 Oct 27 08:11 /etc/group
13. Combine Sed Substitution Flags
You can combine one or more substitute flags as required. The following example will replace all occurrences of "Manager" or "manager" to "Director". This will also print only the line that was changed by the substitute command to the screen, and write the same information to the output.txt file. Combine g,i,p and w flags:$ sed -n 's/Manager/Director/gipw output.txt' employee.txt 102,Jason Smith,IT Director 105,Jane Miller,Sales Director $ cat output.txt 102,Jason Smith,IT Director 105,Jane Miller,Sales Director
14. Sed Substitution Delimiter
In all the above sed examples, we used the default sed delimiter /. i.e. s/original-string/replacement-string/ When there is a slash / in the original-string or the replacement-string, we need to escape it using \. For this example create a path.txt file which contains a directory path as shown below.
$ vi path.txt reading /usr/local/bin directory
Now, let us change /usr/local/bin to /usr/bin using the sed substitute command. In this sed substitution example, the delimiter path delimiter ‘/’ was escaped using back slash '\' in the original-string and the replacement-string.
$ sed 's/\/usr\/local\/bin/\/usr\/bin/' path.txt reading /usr/bin directory
Ugly isn't it? When you are trying to replace a long path name, it might be very confusing to use all those escape characters '\'. Fortunately, you can use any character as substitution delimiter. For example, | or ^ or @ or !.
All of the following are valid and easy to read. I have not shown the output of the commands since they all produce exactly the same result. I prefer to use @ (or !) symbol when replacing a directory path but it is your personal choice.
sed 's|/usr/local/bin|/usr/bin|' path.txt sed 's^/usr/local/bin^/usr/bin^' path.txt sed 's@/usr/local/bin@/usr/bin@' path.txt sed 's!/usr/local/bin!/usr/bin!' path.txt
15. Multiple Substitute Commands Affecting the Same Line
As we discussed earlier, the sed execution flow is Read, Execute, Print, Repeat. The Execute portion, as we mentioned, may consist of multiple sed commands, which sed will execute one-by-one. For example, if you have two sed commands, sed will execute command-1 on the pattern space, then execute command-2 on the pattern space. If command-1 changed something in the pattern space, command-2 will be executed on the newly changed pattern space (and not the original line that was Read). The following example demonstrates the execution of two sed substitute commands on the pattern space. Change Developer to IT Manager, then change Manager to Director:$ sed '{ s/Developer/IT Manager/ s/Manager/Director/ }' employee.txt 101,John Doe,CEO 102,Jason Smith,IT Director 103,Raj Reddy,Sysadmin 104,Anand Ram,IT Director 105,Jane Miller,Sales DirectorLet us analyze the sed execution flow for line 4 in the example. 1. Read: At this stage, Sed reads the line and puts it in the pattern space. So, the following is the content of the pattern space.
104,Anand Ram,Developer2. Execute: Sed executes the 1st sed command on the pattern space, which is s/Developer/IT Manager/. So, after this command, the following is the content of the pattern space.
104,Anand Ram,IT ManagerNow, sed executes the 2nd sed command on the pattern space, which is s/Manager/Director/. After this command, the following is the content of the pattern space.
104,Anand Ram,IT DirectorRemember: Sed executes the 2nd command on the content of the current pattern space after execution of the first command. 3. Print: It prints the content of the current pattern space, which is the following.
104,Anand Ram,IT Director4. Repeat: It moves on to the next line and repeats from step#1.
16. Power of & - Get Matched Pattern
When & is used in the replacement-string, it replaces it with whatever text matched the original-string or the regular-expression. This is very powerful and useful. The following are few examples. Enclose the employee id (the 1st three numbers) between [ and ], i.e. 101 becomes [101], 102 becomes [102], etc.$ sed 's/^[0-9][0-9][0-9]/[&]/g' employee.txt [101],John Doe,CEO [102],Jason Smith,IT Manager [103],Raj Reddy,Sysadmin [104],Anand Ram,Developer [105],Jane Miller,Sales ManagerEnclose the whole input line between < and >
$ sed 's/^.*/<&>/' employee.txt <101,John Doe,CEO> <102,Jason Smith,IT Manager> <103,Raj Reddy,Sysadmin> <104,Anand Ram,Developer> <105,Jane Miller,Sales Manager>
17. Substitution Grouping (Single Group)
Grouping can be used in sed just like in a normal regular expression. A group is opened with “\(” and closed with “\)”. Grouping can be used in combination with back-referencing. A back-reference is the re-use of a part of a regular expression selected by grouping. Back-references in sed can be used in both a regular expression and in the replacement part of the substitute command.Single grouping:
$ sed 's/\([^,]*\).*/\1/g' employee.txt 101 102 103 104 105In the above example:
- Regular expression \([^,]*\) matches the string up to the 1st comma.
- \1 in the replacement-string replaces the first matched group.
- g is the global substitute flag.
sed 's/\([^:]*\).*/\1/' /etc/passwdThe following example encloses the 1st letter in every word inside (), if the 1st character is upper case.
$ echo "The Geek Stuff" | sed 's/\(\b[A-Z]\)/\(\1\)/g' (T)he (G)eek (S)tuffFor the next example create a numbers.txt sample file as shown below.
$ vi numbers.txt 1 12 123 1234 12345 123456Commify numbers, i.e. insert commas to make them more readable:
$ sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\ {3\}\)/\1\2,\3/g' numbers.txt 1 12 123 1,234 12,345 123,456The above command should be executed in a single line as shown below.
sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g' numbers.txt
18. Substitution Grouping (Multiple Group)
In multi grouping, you can have multiple groups enclosed in multiple “\(” and “\)”. When you have multiple groups in the substitute regular expression, you can use \n to specify the nth group in the sed replacement string. An example is shown below. Get only the 1st column (employee id) and the 3rd column (title):$ sed 's/\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/g' employee.txt 101,CEO 102,IT Manager 103,Sysadmin 104,Developer 105,Sales ManagerThe above command should be executed in a single line as shown below.
sed 's/\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/g' employee.txtIn the above example, you can see three groups mentioned in the original-string (reg-ex). These three groups are separated by commas.
- ([^,]*\) is group 1 that matches the employee id
- , is the field separator after group 1
- ([^,]*\) is group 2 that matches the employee name
- , is the field separator after group 2
- ([^,]*\) is group 3 that matches the employee title
- , is the field separator after group 3 The replacement-string section of the above example indicates how these groups should be used.
- \1 is to print group 1 (employee id)
- , is to print a comma after printing group 1
- \3 is to print group 1 (title)
$ sed 's/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1,\3/g' employee.txt John Doe,101,CEO Jason Smith,102,IT Manager Raj Reddy,103,Sysadmin Anand Ram,104,Developer Jane Miller,105,Sales ManagerThe above command should be executed in a single line as shown below.
sed 's/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1,\3/g' employee.txt
19. Gnu Sed Only Replacement String Flags
These flags are available only in GNU version of sed. They can be used in the replacement-string part of the sed substitute command.\l replacement string flag
When you specify \l in the replacement-string part, it treats the character that immediately follows \l as lower case. You already know the following simple example will change John to JOHNNY.sed 's/John/JOHNNY/' employee.txtThe following example contains \l before H in the replacement-string (i.e. JO\lHNNY). This will change only the character h in JOHNNY to lower case. Change John to JOhNNY:
$ sed -n 's/John/JO\lHNNY/p' employee.txt 101,JOhNNY Doe,CEO
\L replacement string flag
When you specify \L in the replacement-string part, it treats the rest of the characters as lower case. The following example contains \L before H in the replacement-string (i.e. JO\lHNNY). This will change the rest of the characters from h to lower case. Change Johnny to JOhnny:$ sed -n 's/John/JO\LHNNY/p' employee.txt 101,JOhnny Doe,CEO
\u replacement string flag
Just like \l, but for upper case. When you specify \l in the replacement-string part, it treats the character that immediately follows \u as upper case. The following example contains \u before h in the replacement-string (i.e. jo\uhnny). This will change only the character h in johnny to upper case. Change John to joHnny:$ sed -n 's/John/jo\uhnny/p' employee.txt 101,joHnny Doe,CEO
\U replacement string flag
When you specify \U in the replacement-string part, it treats the rest of the characters as upper case. The following example contains \U before h in the replacement-string (i.e. jo\Uhnny). This will change the rest of the characters from h in johnny to upper case. Change John to joHNNY:$ sed -n 's/John/jo\Uhnny/p' employee.txt 101,joHNNY Doe,CEO
\E replacement string flag
This should be used in conjunction with either \L or \U. This stops the conversion initiated by either \L or \U. The following example prints the whole replacement string "Johnny Boy" in upper case, as we have \U at the beginning of the replacement-string. Change John to JOHNNY BOY:$ sed -n 's/John/\UJohnny Boy/p' employee.txt 101,JOHNNY BOY Doe,CEOChange John to JOHNNY Boy:
$ sed -n 's/John/\UJohnny\E Boy/p' employee.txt 101,JOHNNY Boy Doe,CEOThe above example prints only "Johnny" in the upper case, as we have \E immediately after "Johnny" in the replacement-string.
Replacement String Flag Usages
The above static examples are shown only to understand how these switches works. However, the flags don't have much value when used with static values, as you can just type the static values in the exact case needed. The flags are quite useful when combined with grouping. In the previous example we learned how to swap field 1 with field 3 using grouping. You can convert a whole grouping to either upper or lower case using these switches. Employee name in all upper case, and title in all lower case:$ sed 's/\([^,]*\),\([^,]*\),\(.*\).*/\U\2\E,\1,\L\3/g' employee.txt JOHN DOE,101,ceo JASON SMITH,102,it manager RAJ REDDY,103,sysadmin ANAND RAM,104,developer JANE MILLER,105,sales managerThe above command should be executed in a single line as shown below.
sed 's/\([^,]*\),\([^,]*\),\(.*\).*/\U\2\E,\1,\L\3/g' employee.txtIn the above example, in the replacement-string, we have the following:
- \U\2\E - This indicates that this field, which is the 2nd group (employee name), should be converted to upper case. \U start the upper case conversion, and \E stops it.
- \L\3 - This indicates that this field, which is 3rd group (title), should be converted to lower case. \L starts the lower case conversion for rest of the characters.