1. Replacing all occurrences of one string with another in all files in the current directory:
These are for cases where you know that the directory contains only regular files and that you want to process all non-hidden files. If that is not the case, use the approaches in 2.
All sed
solutions in this answer assume GNU sed
. If using FreeBSD or OS/X, replace -i
with -i ''
. Also note that the use of the -i
switch with any version of sed
has certain filesystem security implications and is inadvisable in any script which you plan to distribute in any way.
-
Non recursive, files in this directory only:
sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./*
(the perl
one will fail for file names ending in |
or space)).
-
Recursive, regular files (including hidden ones) in this and all subdirectories
find . -type f -exec sed -i 's/foo/bar/g' {} +
If you are using zsh:
sed -i -- 's/foo/bar/g' **/*(D.)
(may fail if the list is too big, see zargs
to work around).
Bash can't check directly for regular files, a loop is needed (braces avoid setting the options globally):
( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done )
The files are selected when they are actual files (-f) and they are writable (-w).
4. Multiple replace operations: replace with different strings
-
You can combine sed
commands:
sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
Be aware that order matters (sed 's/foo/bar/g; s/bar/baz/g'
will substitute foo
with baz
).
-
or Perl commands
perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
-
If you have a large number of patterns, it is easier to save your patterns and their replacements in a sed
script file:
#! /usr/bin/sed -f
s/foo/bar/g s/baz/zab/g
-
Or, if you have too many pattern pairs for the above to be feasible, you can read pattern pairs from a file (two space separated patterns, $pattern and $replacement, per line):
while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt
-
That will be quite slow for long lists of patterns and large data files so you might want to read the patterns and create a sed
script from them instead. The following assumes a <space> delimiter separates a list of MATCH<space>REPLACE pairs occurring one-per-line in the file patterns.txt
:
sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <patterns.txt | sed -f- ./editfile >outfile
The above format is largely arbitrary and, for example, doesn't allow for a <space> in either ofMATCH or REPLACE. The method is very general though: basically, if you can create an output stream which looks like a sed
script, then you can source that stream as a sed
script by specifying sed
's script file as -
stdin.
-
You can combine and concatenate multiple scripts in similar fashion:
SOME_PIPELINE |
sed -e'#some expression script' \ -f./script_file -f- \ -e'#more inline expressions' \ ./actual_edit_file >./outfile
A POSIX sed
will concatenate all scripts into one in the order they appear on the command-line. None of these need end in a \n
ewline.
-
grep
can work the same way:
sed -e'#generate a pattern list' <in | grep -f- ./grepped_file
-
When working with fixed-strings as patterns, it is good practice to escape regular expressionmetacharacters. You can do this rather easily:
sed 's/[]$&^*\./[]/\\&/g
s| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|
' <patterns.txt | sed -f- ./editfile >outfile
5. Multiple replace operations: replace multiple patterns with the same string
-
Replace any of foo
, bar
or baz
with foobar
sed -Ei 's/foo|bar|baz/foobar/g' file
-
or
perl -i -pe 's/foo|bar|baz/foobar/g' file