4.41. How do I make substitutions in every file in a directory, or in a complete directory tree?
4.41.1. - ssed and Perl solution
The best solution for multiple files in a single directory is to
use ssed or gsed v4.0 or higher:
sed -i.BAK 's|foo|bar|g' files # -i does in-place replacement
If you don't have ssed, there is a similar solution in Perl. (Yes,
we know this is a FAQ file for sed, not perl, but perl is more
common than ssed for many users.)
perl -pi.bak -e 's|foo|bar|g' files # or
perl -pi.bak -e 's|foo|bar|g' `find /pathname -name "filespec"`
For each file in the filelist, sed (or Perl) renames the source
file to "filename.bak"; the modified file gets the original
filename. Remove '.bak' 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 let you replace '/some/path' with
'/another/path', accommodating slashes in the LHS and RHS.)
To recurse directories in Unix or GNU/Linux:
# We use xargs to prevent passing too many filenames to sed, but
# this command will fail if filenames contain spaces or newlines.
find /my/path -name '*.ht' -print | xargs sed -i.BAK 's|foo|bar|g'
To recurse directories under Windows 2000 (CMD.EXE or COMMAND.COM):
# This syntax isn't supported under Windows 9x COMMAND.COM
for /R c:\my\path %f in (*.htm) do sed -i.BAK "s|foo|bar|g" %f
4.41.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 <[email protected]> for this script):
#! /bin/sh
# filename: replaceall
# Backup files are NOT saved in this script.
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.41.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 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,
located at <ftp://garbo.uwasa.fi/pc/link/tsbat.zip> (this file is
regularly updated). Another alternative is to 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.
https://hobbes.nmsu.edu/pub/os2/util/disk/forall72.zip
ftp://garbo.uwasa.fi/pc/filefind/target15.zip