4. EXAMPLES 4.1. How do I perform a case-insensitive search? Use GNU sed v3.02 (or higher) with the I flag ("/regex/I" or "s/LHS/RHS/I"). Or use sedmod with the -i switch on the command line. With other versions of sed this is not easy to do, so some people use GNU awk (gawk), mawk, or perl, since these programs have options for case-insensitive searches. In gawk/mawk, use "BEGIN {IGNORECASE=1}" and in perl, "/regex/i". For sed, here are three solutions: Solution 1: convert everything to upper case and search normally # sed script, solution 1 h; # copy the original line to the hold space # convert the pattern space to solid caps y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ # now we can search for the word "CARLOS" /CARLOS/ { # add or insert lines. Note: "s/.../.../" will not work # here because we are searching a modified pattern # space and are not printing the pattern space. } x; # get back the original pattern space # the original pattern space will be printed Solution 2: search for both cases Often, proper names will either start with all lower-case ("unix"), with an initial capital letter ("Unix") or occur in solid caps ("UNIX"). There may be no need to search for every possibility. /UNIX/b match /[Uu]nix/b match Solution 3: search for all possible cases # If all else fails, search for any possible combination /[Ca][Aa][Rr][Ll][Oo][Ss]/... Bear in mind that as the pattern length increases, this solution becomes an order of magnitude slower than the one of Solution 1, at least with some implementations of sed. 4.2. How do I make changes in only part of a file? Select parts of a file for changing by naming a range of lines either by number (e.g., lines 1-20), by RE (between the words "foo" and "bar"), or by some combination of the two. For multiple changes, put the substitution command between braces {...}. # replace only between lines 1 and 20 1,20 s/Johnson/White/g # replace everywhere EXCEPT between lines 1 and 20 1,20 !s/Johnson/White/g # replace only between words "foo" and "bar" /foo/,/bar/ { s/Johnson/White/g; s/Smith/Wesson/g; } # replace only from the words "ENDNOTES:" to the end of file /ENDNOTES:/,$ { s/Schaff/Herzog/g; s/Kraft/Ebbing/g; } For technical details on using address ranges, see section 3.3 ("Addressing and Address ranges"). 4.3. How do I change only the first occurrence of a pattern? To replace the regex "LHS" with "RHS", do this: gsed '0,/LHS/s//RHS/' # GNU sed 3.02a sed -e '1s/LHS/RHS/;t' -e '1,/LHS/s//RHS/' # other seds If you know the pattern won't occur on the first line, omit the first -e and the statement following it. 4.4. How do I make substitutions in every file in a directory, or in a complete directory tree? 4.4.1. - Perl solution (Yes, we know this is a FAQ file for sed, not perl, but the solution is so simple that it has to be noted. Also, perl and sed share a very similar syntax here.) perl -pi.bak -e 's|foo|bar|g' filelist # or perl -pi.bak -e 's|foo|bar|g' `find /pathname -name "filespec"` For each file in the filelist, perl renames the source file to "filename.bak"; the modified file gets the original filename. Change '-pi.bak' to '-pi' if you don't need backup copies. (Note the use of s||| instead of s/// here, and in the scripts below. The vertical bars in the 's' command lets you replace '/some/path' with '/another/path', accommodating slashes in the LHS and RHS.) 4.4.2. - Unix solution For all files in a single directory, assuming they end with *.txt and you have no files named "[anything].txt.bak" already, use a shell script: #! /bin/sh # Source files are saved as "filename.txt.bak" in case of error # The '&&' after cp is an additional safety feature for file in *.txt do cp $file $file.bak && sed 's|foo|bar|g' $file.bak >$file done To do an entire directory tree, use the Unix utility find, like so (thanks to Jim Dennis <[131]jadestar@rahul.net> for this script): #! /bin/sh # filename: replaceall find . -type f -name '*.txt' -print | while read i do sed 's|foo|bar|g' $i > $i.tmp && mv $i.tmp $i done This previous shell script recurses through the directory tree, finding only files in the directory (not symbolic links, which will be encountered by the shell command "for file in *.txt", above). To preserve file permissions and make backup copies, use the 2-line cp routine of the earlier script instead of "sed ... && mv ...". By replacing the sed command 's|foo|bar|g' with something like sed "s|$1|$2|g" ${i}.bak > $i using double quotes instead of single quotes, the user can also employ positional parameters on the shell script command tail, thus reusing the script from time to time. For example, replaceall East West would modify all your *.txt files in the current directory. 4.4.3. - DOS solution: MS-DOS users should use two batch files like this: @echo off :: MS-DOS filename: REPLACE.BAT :: :: Create a destination directory to put the new files. :: Note: The next command will fail under Novel Netware :: below version 4.10 unless "SHOW DOTS=ON" is active. if not exist .\NEWFILES\NUL mkdir NEWFILES for %%f in (*.txt) do CALL REPL_2.BAT %%f echo Done!! :: =======End of the first batch file==== @echo off :: MS-DOS filename: REPL_2.BAT :: sed "s/foo/bar/g" %1 > NEWFILES\%1 :: =======End of the second batch file=== When finished, the current directory contains all the original files, and the newly-created NEWFILES subdirectory contains the modified *.TXT files. Do not attempt a command like for %%f in (*.txt) do sed "s/foo/bar/g" %%f >NEWFILES\%%f under any version of MS-DOS because the output filename will be created as a literal '%f' in the NEWFILES directory before the %%f is expanded to become each filename in (*.txt). This occurs because MS-DOS creates output filenames via redirection commands before it expands "for..in..do" variables. To recurse through an entire directory tree in MS-DOS requires a batch file more complex than we have room to describe. Examine the file SWEEP.BAT in Timo Salmi's great archive of batch tricks, TSBAT61.ZIP, located at <[132]ftp://garbo.uwasa.fi/pc/ts/tsbat61.zip>, or get an external program designed for directory recursion. Here are some recommended programs for directory recursion. The first one, FORALL, runs under either OS/2 or DOS. Unfortunately, none of these supports Win9x long filenames. [133]ftp://hobbes.nmsu.edu/pub/os2/util/disk/forall72.zip [134]http://www.geocities.com/SiliconValley/Lakes/2414/fortn711.zip [135]http://garbo.uwasa.fi/pc/filefind/target15.zip 4.5. How do I parse a comma-delimited data file? Comma-delimited data files can come in several forms, requiring increasing levels of complexity in parsing and handling: (a) No quotes, no internal commas 1001,John Smith,PO Box 123,Chicago,IL,60699 1002,Mary Jones,320 Main,Denver,CO,84100, (b) Like (a), with quotes around each field "1003","John Smith","PO Box 123","Chicago","IL","60699" "1004","Mary Jones","320 Main","Denver","CO","84100" (c) Like (b), with embedded commas "1005","Tom Hall, Jr.","61 Ash Ct.","Niles","OH","44446" "1006","Bob Davis","429 Pine, Apt. 5","Boston","MA","02128" (d) Like (c), with embedded commas and quotes "1007","Sue "Red" Smith","19 Main","Troy","MI","48055" "1008","Joe "Hey, guy!" Hall","POB 44","Reno","NV","89504" In each example above, we have 7 fields and 6 commas which function as field separators. Case (c) is a very typical form of these data files, with double quotes used to enclose each field and to protect internal commas (such as "Tom Hall, Jr.") from interpretation as field separators. However, many times the data may include both embedded quotation marks as well as embedded commas, as seen by case (d), above. Before handling a comma-delimited data file, make sure that you fully understand its format and check the integrity of the data. Does each line contain the same number of fields? Should certain fields be composed only of numbers or of two-letter state abbreviations in all caps? Sed (or awk or perl) should be used to validate the integrity of the data file before you attempt to alter it or extract particular fields from the file. After ensuring that each line has a valid number of fields, use sed to locate and modify individual fields, using the \(...\) grouping command where needed. In case (a): sed 's/^[^,]*,[^,]*,[^,]*,[^,]*,/.../' ^ ^ ^ | | |_ 3rd field | |_______ 2nd field |_____________ 1st field # Unix script to delete the second field for case (a) sed 's/^\([^,]*\),[^,]*,/\1,,/' file # Unix script to change field 1 to 9999 for case (a) sed 's/^[^,]*,/9999,/' file In cases (b) and (c): sed 's/^"[^"]*","[^"]*","[^"]*","[^"]*",/.../' 1st-- 2nd-- 3rd-- 4th-- # Unix script to delete the second field for case (c) sed 's/^\("[^"]*"\),"[^"]*",/\1,"",/' file # Unix script to change field 1 to 9999 for case (c) sed 's/^"[^"]*",/"9999",/' file In case (d): One way to parse such files is to replace the 3-character field separator "," with an unused character like the tab or vertical bar. (Technically, the field separator is only the comma while the fields are surrounded by "double quotes", but the net effect is that fields are separated by quote-comma-quote, with quote characters added to the beginning and end of each record.) Search your datafile first to make sure that your character appears nowhere in it! sed -n '/|/p' file # search for any instance of '|' # if it's not found, we can use the '|' to separate fields Then replace the 3-character field separator and parse as before: # sed script to delete the second field for case (d) s/","/|/g; # global change of "," to bar s/^\([^|]*\)|[^|]|/\1||/; # delete 2nd field s/|/","/g; # global change of bar back to "," # sed script to change field 1 to 9999 for case (d) # Remember to accommodate leading and trailing quote marks s/","/|/g; s/^[^|]*|/"9999|/; s/|/","/g; Note that this technique works only if each and every field is surrounded with double quotes, including empty fields. If your datafile does not look like case (d), above, or if it omits quote marks around empty fields or numeric values, then the complexity of the script would probably not be worth the effort to write it in sed. For such a case, you should use perl. This question is addressed in the Perl FAQ, at question 4.28: "How can I split a [character] delimited string except when inside [character]?" 4.6. How do I insert a newline into the RHS of a substitution? Six versions of sed permit '\n' to be typed directly into the RHS, which is then converted to a newline on output: gsed-3.02.80, gsed-3.02a, gsed103 (with the -x switch), HHsed (a/k/a sed14), sedmod, and UnixDOS sed. The easiest solution is to use one of these versions. For other versions of sed, try one of the following: (a) Insert an unused character and pipe the output through tr: echo twolines | sed 's/two/& new=/' | tr "=" "\n" # produces two new lines (b) Use two backslashes (\\) from the shell prompt. Using bash: [bash-prompt]$ echo twolines | sed "s/two/& new\\ >/" two new lines [bash-prompt]$ (c) Write a multi-line script and use the backslash (\) in the middle of the "replace" portion: sed -f newline.sed files # newline.sed s/twolines/two new\ lines/g Some versions of sed may not need the trailing backslash. If so, remove it. (d) Use the "G" command: G appends a newline, plus the contents of the hold space to the end of the pattern space. If the hold space is empty, a newline is appended anyway. The newline is stored in the pattern space as "\n" where it can be addressed by grouping "\(...\)" and moved in the RHS. Thus, to change the "twolines" example used earlier, the following script will work: sed '/twolines/{G;s/\(two\)\(lines\)\(\n\)/\1\3\2/;}' (e) Inserting full lines, not breaking lines up: If one is not changing lines but only inserting complete lines before or after a pattern, the procedure is much easier. Use the "i" (insert) or "a" (append) command, making the alterations by an external script. To insert "This line is new" BEFORE each line matching a regex: /RE/i This line is new # HHsed, sedmod, gsed 3.02a /RE/{x;s/.*/This line is new/;G;} # other seds To append "This line is new" AFTER each line matching a regex: /RE/a This line is new # HHsed, sedmod, gsed 3.02a /RE/{G;s/$/This line is new/;} # other seds To append 2 blank lines after each line matching a regex: /RE/{G;G;} # assumes the hold space is empty To replace each line matching a regex with 5 blank lines: /RE/{s/.*//;G;G;G;G;} # assumes the hold space is empty (f) Use the "y///" command if possible: On some Unix versions of sed (not GNU sed!), though the s/// command won't accept '\n' in the RHS, the y/// command does. If your Unix sed supports it, a newline after "aaa" can be inserted this way (which is not portable to GNU sed or other seds): s/aaa/&~/; y/~/\n/; # assuming no other '~' is on the line! 4.7. How do I represent control-codes or nonprintable characters? GNU sed v3.02.80, GNU sed v1.03, and HHsed v1.5 by Howard Helman all support all support the notation \xNN, where "NN" are two valid hex numbers, 00-FF. sed is not intended to process binary or object code, and files which contain nulls (0x00) will usually generate errors in most versions of sed (GNU sed 3.02a is an exception; it allows nulls in the input files and also in regexes). On Unix platforms, the 'echo' command may allow insertion of octal or hex values, e.g., `echo "\0nnn"` or `echo -n "\0nnn"`. The echo command may also support syntax like '\\b' or '\\t' for backspace or tab characters. Check the man pages to see what syntax your version of echo supports. Some versions support the following: # replace 0x1A (32 octal) with ASCII letters sed 's/'`echo "\032"`'/Ctrl-Z/g' # note the 3 backslashes in the command below sed "s/.`echo \\\b`//g" 4.8. How do I read environment variables with sed? 4.8.1. - on Unix platforms In Unix, environment variables are words which begin with a dollar sign, such as $TERM, $HOME, $user, or $path. In sed, the dollar sign is used to indicate the last line of the input file, the end of a line (in the LHS), or a literal symbol (in the RHS). Sed cannot access variables directly, so one must pay attention to shell quoting requirements to expand the variables properly. To ALLOW the Unix shell to interpret the dollar sign (replacing it with an environment variable), put the script in double quotes: sed "s/_terminal-type_/$TERM/g" input.file >output.file To PREVENT the Unix shell from interpreting the dollar sign (letting sed define its meaning), put the script in single quotes: sed 's/.$//' DOS.file >Unix.file To use BOTH Unix $environment_vars and sed /end-of-line$/ pattern matching, use single quotes to bracket the sed part 'like so', then follow immediately with double quotes "$HERE" when you want the shell to substitute the variable, and resume with single quotes again where 'sed should set the meaning'. There must be NO SPACE between the closing single quotes and the opening double quotes. To demonstrate with the example two sentences above: sed 'like so'"$HERE"'sed should set the meaning' # rough idea sed "s/$user"'$/root/' input.file >output.file # sample use In the sample use above, we search for the user's name (which is stored as an environment variable) when it occurs at the end of the line ($), and we substitute the word "root" in all these occasions. In writing shell scripts, we likewise begin with single quote marks ('), close them upon encountering the variable, enclose the variable name in double quotes ("), and resume with single quotes, closing them at the end of the sed script. Example: #! /bin/sh # lower to upper, that could be changed FROM='abcdefgh' TO='ABCDEFGH' ... misc commands that pipe data into a longer sed script. sed ' ... # do the conversion y/'"$FROM"'/'"$TO"'/ # some more commands go here . . . # last line is a single quote mark ' Thus, each variable named $FROM is replaced by $TO, and the single quotes are used to glue the multiple lines together in the script. (See also section 4.10, "How do I handle shell quoting in sed?") 4.8.2. - on MS-DOS and 4DOS platforms Under 4DOS and MS-DOS version 7.0 (Win95) or 7.10 (Win95 OSR2), environment variables can be accessed from the command prompt. Under MS-DOS 6.22 and below, environment variables can only be accessed from within batch files. Environment variables should be enclosed between percent signs and are case-insensitive; i.e., %USER% or %user% will display the USER variable. To generate a true percent sign, just enter it twice. DOS versions of sed require that sed scripts be enclosed by double quote marks "..." (not single quotes!) if the script contains embedded tabs, spaces, redirection arrows or the vertical bar. In fact, if the input for sed comes from piping, a sed script should not contain a vertical bar, even if it is protected by double quotes (this seems to be bug in the normal MS-DOS syntax). Thus, echo blurk | sed "s/^/ |foo /" # will cause an error sed "s/^/ |foo /" blurk.txt # will work as expected Using DOS environment variables which contain DOS path statements (such as a TMP variable set to "C:\TEMP") within sed scripts is discouraged because sed will interpret the backslash '\' as a metacharacter to "quote" the next character, not as a normal symbol. Thus, sed "s/^/%TMP% /" somefile.txt will not prefix each line with (say) "C:\TEMP ", but will prefix each line with "C:TEMP "; sed will discard the backslash, which is probably not what you want. Other variables such as %PATH% and %COMSPEC% will also lose the backslash within sed scripts. Environment variables which do not use backslashes are usually workable. Thus, all the following should work without difficulty, if they are invoked from within DOS batch files: sed "s/=username=/%USER%/g" somefile.txt echo %FILENAME% | sed "s/\.TXT/.BAK/" grep -Ei "%string%" somefile.txt | sed "s/^/ /" while from either the DOS prompt or from within a batch file, sed "s/%%/ percent/g" input.fil >output.fil will replace each percent symbol in a file with " percent" (adding the leading space for readability). 4.9. How do I export or pass variables back into the environment? 4.9.1. - on Unix platforms Suppose that line #1, word #2 of the file 'terminals' contains a value to be put in your TERM environment variable. Sed cannot export variables directly to the shell, but it can pass strings to shell commands. To set a variable in the Bourne shell: TERM=`sed 's/^[^ ][^ ]* \([^ ][^ ]*\).*/\1/;q' terminals`; export TERM If the second word were "Wyse50", this would send the shell command "TERM=Wyse50". 4.9.2. - on MS-DOS or 4DOS platforms Sed cannot directly manipulate the environment. Under DOS, only batch files (.BAT) can do this, using the SET instruction, since they are run directly by the command shell. Under 4DOS, special 4DOS commands (such as ESET) can also alter the environment. Under DOS or 4DOS, sed can select a word and pass it to the SET command. Suppose you want the 1st word of the 2nd line of MY.DAT put into an environment variable named %PHONE%. You might do this: @echo off sed -n "2 s/^\([^ ][^ ]*\) .*/SET PHONE=\1/p;3q" MY.DAT > GO_.BAT call GO_.BAT echo The environment variable for PHONE is %PHONE% :: cleanup del GO_.BAT The sed script assumes that the first character on the 2nd line is not a space and uses grouping \(...\) to save the first string of non-space characters as \1 for the RHS. In writing any batch files, make sure that output filenames such as GO_.BAT don't overwrite preexisting files of the same name. 4.10. How do I handle Unix shell quoting in sed? To embed a literal single quote (') in a script, use (a) or (b): (a) If possible, put the script in double quotes: sed "s/cannot/can't/g" file (b) If the script must use single quotes, then close-single-quote the script just before the SPECIAL single quote, prefix the single quote with a backslash, and use a 2nd pair of single quotes to finish marking the script. Thus: sed 's/cannot$/can'\''t/g' file Though this looks hard to read, it breaks down to 3 parts: 's/cannot$/can' \' 't/g' --------------- -- ----- To embed a literal double quote (") in a script, use (a) or (b): (a) If possible, put the script in single quotes. You don't need to prefix the double quotes with anything. Thus: sed 's/14"/fourteen inches/g' file (b) If the script must use double quotes, then prefix the SPECIAL double quote with a backslash (\). Thus, sed "s/$length\"/$length inches/g" file To embed a literal backslash (\) into a script, enter it twice: sed 's/C:\\DOS/D:\\DOS/g' config.sys 4.11. How do I delete a block of text if the block contains a certain regular expression? The following deletes the block between 'start' and 'end' inclusively, if and only if the block contains the string (optionally a pattern) 'regex'. Written by Russell Davies <[136]r@itntl.bhp.com.au>, with comments by the FAQ maintainer: :t /start/,/end/ { # For each line between these block markers.. /end/!{ # If we are not at the /end/ marker $!{ # nor the last line of the file, N; # add the Next line to the pattern space bt } # and branch (loop back) to the :t label. } # This line matches the /end/ marker. /regex/d; # If /regex/ matches, delete the block. } # Otherwise, the block will be printed. 4.12. How do I locate/print a paragraph of text if the paragraph contains a certain regular expression? Assume that paragraphs are separated by blank lines. For regexes that are single terms, use the following script: sed -e '/./{H;$!d;}' -e 'x;/regex/!d' To print paragraphs only if they contain 3 specific regular expressions (RE1, RE2, and RE3), in any order in the paragraph: sed -e '/./{H;$!d;}' -e 'x;/RE1/!d;/RE2/!d;/RE3/!d' With this solution and the preceding one, if the paragraphs are excessively long (more than 4k in length), you may overflow sed's internal buffers. If using HHsed, you must add a "G;" command immediately after the "x;" in the scripts above to defeat a bug in HHsed (see section 6.7.F(5), below, for a description). 4.13. How do I delete a block of specific consecutive lines? If the block of lines always looks like this (with '^' and '$' representing the beginning and end of line, respectively): ^able$ ^baker$ ^charlie$ ^delta$ and if there is never any deviation from this format (e.g., "able" always is followed by "baker", etc.), this will work fine: sed '/^able$/,/^delta$/d' files # most seds sed '/^able$/,+3d' files # HHsed, sedmod, gsed 3.02.80 However, if the top line sometimes appears alone or is followed by other lines, if the block may have additional lines in the middle, or if a partial block could possibly occur somewhere in the file, a more explicit script is needed. The following scripts show how to delete blocks of specific consecutive lines. Only an exact match of the block is deleted, and partial matches of the block are left alone. # sed script to delete 2 consecutive lines: /^RE1\nRE2$/ $b /^RE1$/ { $!N /^RE1\nRE2$/d P;D } #---end of script--- # sed script to delete 3 consecutive lines. (This script # fails under GNU sed earlier than version 3.02.) : more $!N s/\n/&/2; t enough $!b more : enough /^RE1\nRE2\nRE3$/d P;D #---end of script--- For example, to delete a block of 5 consecutive lines, the previous script must be altered in only two places: (1) Change the 2 in "s/\n/&/2;" to a 4 (the trailing semicolon is needed to work around a bug in HHsed v1.5). (2) Change the regex line to "/^RE1\nRE2\nRE3\nRE4\nRE5$/d", modifying the expression as needed. Suppose we want to delete a block of two blank lines followed by the word "foo" followed by another blank line (4 lines in all). Other blank lines and other instances of "foo" should be left alone. After changing the '2' to a '3' (always one number less than the total number of lines), the regex line would look like this: "/^\n\nfoo\n$/d". (Thanks to Greg Ubben for this script.) As an alternative for older versions of GNU sed, the following script will delete 4 consecutive lines: # sed script to delete 4 consecutive lines (gsed-2.05 and below) /^RE1$/!b $!N $!N :a $b N /^RE1\nRE2\nRE3\nRE4$/d P s/^.*\n\(.*\n.*\n.*\)$/\1/ ba #---end of script--- Its drawback is that it must be modified in 3 places instead of 2 to adapt it for more lines, and as additional lines are added, the 's' command is forced to work harder to match the regexes. On the other hand, it avoids a problem with gsed-2.05 and shows another way to solve the problem of deleting consecutive lines. 4.14. How do I read (insert/add) a file at the top of a textfile? Given a textfile, file1, one may wish to prepend or insert an external file, fileT, to the top of it before processing the file. Normally, this should be done from the Unix or DOS shell before passing file1 on to sed (MS-DOS 5.0 or lower needs 3 commands to do this; for DOS 6.0 or higher, the MOVE command is available): copy fileT+file1 temp # MS-DOS command 1 echo Y | copy temp file1 # MS-DOS command 2 del temp # MS-DOS command 3 cat fileT file1 >temp; mv temp file1 # Unix commands However, if inserting the file must be done from within sed, there is a way. The expected sed command "1 r fileT" will not work; it first prints line 1 and then inserts fileT between lines 1 and 2. The following two-line sed script solves this problem, although there must be at least 2 lines in file1 for the script to work properly: 1{ h; r fileT; D; } 2{ x; G; } 4.15. How do I address all the lines between RE1 and RE2, excluding the lines themselves? Normally, to address the lines between two regular expressions, RE1 and RE2, one would do this: '/RE1/,/RE2/{commands;}'. Excluding those lines takes an extra step. To put 2 arrows before each line between RE1 and RE2, except for those lines: sed '1,/RE1/!{ /RE2/,/RE1/!s/^/>>/; }' input.fil The preceding script, though short, may be difficult to follow. It also requires that /RE1/ cannot occur on the first line of the input file. The following script, though it's not a one-liner, is easier to read and it permits /RE1/ to appear on the first line: /RE1/,/RE2/{ /RE1/b /RE2/b s/^/>>/ } Contents of input.fil: Output of sed script: aaa aaa bbb bbb RE1 RE1 aaa >>aaa bbb >>bbb ccc >>ccc RE2 RE2 end end 4.16. How do I replace "/some/UNIX/path" in a substitution? Technically, the normal meaning of the slash can be disabled by prefixing it with a backslash. Thus, sed 's/\/some\/UNIX\/path/\/a\/new\/path/g' files But this is hard to read and write. There is a better solution. The s/// substitution command allows '/' to be replaced by any other character (including spaces or alphanumerics). Thus, sed 's|/some/UNIX/path|/a/new/path|g' files and if you are using variable names in a Unix shell script, sed "s|$OLDPATH|$NEWPATH|g" oldfile >newfile 4.17. How do I replace "C:\SOME\DOS\PATH" in a substitution? For MS-DOS users, every backslash must be doubled. Thus, to replace "C:\SOME\DOS\PATH" with "D:\MY\NEW\PATH" -- sed "s|C:\\SOME\\DOS\\PATH|D:\\MY\\NEW\\PATH|g" infile >outfile Remember that DOS pathnames are not case sensitive and can appear in upper or lower case in the input file. If this concerns you, use gsed v3.02 with the "i" flag or sedmod with the -i switch to ignore case on the LHS: @echo off :: sample MS-DOS batch file to alter path statements set old=C:\\SOME\\DOS\\PATH set new=D:\\MY\\NEW\\PATH gsed "s|%old%|%new%|gi" infile >outfile :: or :: sedmod -i "s|%old%|%new%|g" infile >outfile set old= set new= Also, remember that under Win95 long filenames may be stored in two formats: e.g., as "C:\Program Files" or as "C:\PROGRA~1". 4.18. How do I convert files with toggle characters, like +this+, to look like [i]this[/i]? Input files, especially message-oriented text files, often contain toggle characters for emphasis, like ~this~, *this*, or =this=. Sed can make the same input pattern produce alternating output each time it is encountered. Typical needs might be to generate HMTL codes or print codes for boldface, italic, or underscore. This script accomodates multiple occurrences of the toggle pattern on the same line, as well as cases where the pattern starts on one line and finishes several lines later, even at the end of the file: # sed script to convert +this+ to [i]this[/i] :a /+/{ x; # If "+" is found, switch hold and pattern space /^ON/{ # If "ON" is in the (former) hold space, then .. s///; # .. delete it x; # .. switch hold space and pattern space back s|+|[/i]|; # .. turn the next "+" into "[/i]" ba; # .. jump back to label :a and start over } s/^/ON/; # Else, "ON" was not in the hold space; create it x; # Switch hold space and pattern space s|+|[i]|; # Turn the first "+" into "[i]" ba; # Branch to label :a to find another pattern } #---end of script--- This script uses the hold space to create a "flag" to indicate whether the toggle is ON or not. We have added remarks to illustrate the script logic, but in most versions of sed remarks are not permitted after 'b'ranch commands or labels. If you are sure that the +toggle+ characters never cross line boundaries (i.e., never begin on one line and end on another), this script can be reduced to one line: s|+\([^+][^+]*\)+|[i]\1[/i]|g If your toggle pattern contains regex metacharacters (such as * and +, in the case of HHsed), remember to quote them with backslashes. 4.19. How do I delete only the first occurrence of a pattern? To delete only the first line that contains the pattern RE, where "RE" is any regular expression, but leave all other lines containing RE alone, do this: gsed '0,/RE/{//d}' file # GNU sed 3.02.80 sed '/RE/{x;/Y/!{s/^/Y/;h;d;};x;}' file # other seds And if you know the pattern will not occur on line 1 and you don't use GNU sed, this will work: sed '1,/RE/{/RE/d;}' file 4.20. How do I commify a string of numbers? Use the simplest script necessary to accomplish your task. As variations of the line increase, the sed script must become more complex to handle additional conditions. Whole numbers are simplest, followed by decimal formats, followed by embedded words. Case 1: simple strings of whole numbers separated by spaces or commas, with an optional negative sign. To convert this: 4381, -1222333, and 70000: - 44555666 1234567890 words 56890 -234567, and 89222 -999777 345888777666 chars to this: 4,381, -1,222,333, and 70,000: - 44,555,666 1,234,567,890 words 56,890 -234,567, and 89,222 -999,777 345,888,777,666 chars use one of these one-liners: sed ':a;s/\B[0-9]\{3\}\>/,&/;ta' # GNU sed sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta' # other seds Case 2: strings of numbers which may have an embedded decimal point, separated by spaces or commas, with an optional negative sign. To change this: 4381, -6555.1212 and 70000, 7.18281828 44906982.071902 56890 -2345.7778 and 8.0000: -49000000 -1234567.89012 to this: 4,381, -6,555.1212 and 70,000, 7.18281828 44,906,982.071902 56,890 -2,345.7778 and 8.0000: -49,000,000 -1,234,567.89012 use the following command for GNU sed: sed ':a;s/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g;ta' and for other versions of sed: sed -f case2.sed files # case2.sed s/^/ /; # add space to start of line :a s/\( [-0-9]\{1,\}\)\([0-9]\{3\}\)/\1,\2/g ta s/ //; # remove space from start of line #---end of script--- _______________________________________________________________________________ .