3914
Comment:
|
4642
consistency
|
Deletions are marked like this. | Additions are marked like this. |
Line 6: | Line 6: |
# ed -s file <<< $'s/olddomain\.com/newdomain.com/g\nw' # printf '%s\n' 's/olddomain\.com/newdomain.com/g' w | ed -s file # printf 's/olddomain\.com/newdomain.com/g\nw' | ed -s file |
ed -s file <<< $',s/olddomain\.com/newdomain.com/g\nw\nQ' printf '%s\n' ',s/olddomain\.com/newdomain.com/g' w Q | ed -s file printf ',s/olddomain\.com/newdomain.com/g\nw\nQ' | ed -s file }}} A more portable approach omits the here-string: {{{ ed -s file <<! ,s/olddomain\.com/newdomain.com/g w Q ! |
Line 15: | Line 25: |
ed -s "$file" <<< $'s/old/new/g\nw' | [[ -f $file ]] && ed -s "$file" <<< $',s/old/new/g\nw\nQ' |
Line 17: | Line 27: |
}}} To do this recursively, the best way would be to enable globstar in bash 4 (`shopt -s globstar`, a good idea to put this in your `~/.bashrc`) and use: {{{ for file in ./**/*; do [[ -f $file ]] && ed -s "$file" <<< $',s/old/new/g\nw\nQ' done }}} If you don't have bash 4, you can use find. Unfortunately, it's a bit tedious to feed ed stdin for each file hit: {{{ find . -type f -exec bash -c 'printf "%s\n" ",s/old/new/g" w Q | ed -s '"'{}'" \; |
|
Line 22: | Line 46: |
# sed -i 's/old/new/g' ./* # GNU # sed -i '' 's/old/new/g' ./* # BSD # for file in ./*; do sed 's/old/new/g' "$file" > "$file"~; mv "$file"~ "$file"; done # Others |
sed -i 's/old/new/g' ./* # GNU sed -i '' 's/old/new/g' ./* # BSD for file in ./* # Other do [ -f "$file" ] && sed 's/old/new/g' "$file" > "$file~" && mv "$file~" "$file" done |
Line 36: | Line 65: |
find . -type f -exec perl -pi -e 's/old/new/g' {} + | find . -type f -exec perl -pi -e 's/old/new/g' {} \; |
Line 42: | Line 71: |
# Deletes any line containing the perl regex foo | |
Line 43: | Line 73: |
# Deletes any line containing the perl regex foo | |
Line 50: | Line 79: |
's/\bunsigned\b(?!\s+(int|short|long|char))/unsigned long/g' {} + | 's/\bunsigned\b(?!\s+(int|short|long|char))/unsigned long/g' {} \; |
How can I replace a string with another string in all files?
ed is the standard UNIX command-based editor. Here's three commonly-used syntaxes for replacing the string olddomain.com by the string newdomain.com in a file named file. All three commands do the same although the last two incur the minor additional overhead of a subshell.
ed -s file <<< $',s/olddomain\.com/newdomain.com/g\nw\nQ' printf '%s\n' ',s/olddomain\.com/newdomain.com/g' w Q | ed -s file printf ',s/olddomain\.com/newdomain.com/g\nw\nQ' | ed -s file
A more portable approach omits the here-string:
ed -s file <<! ,s/olddomain\.com/newdomain.com/g w Q !
To replace a string in all files of the current directory:
for file in ./*; do [[ -f $file ]] && ed -s "$file" <<< $',s/old/new/g\nw\nQ' done
To do this recursively, the best way would be to enable globstar in bash 4 (shopt -s globstar, a good idea to put this in your ~/.bashrc) and use:
for file in ./**/*; do [[ -f $file ]] && ed -s "$file" <<< $',s/old/new/g\nw\nQ' done
If you don't have bash 4, you can use find. Unfortunately, it's a bit tedious to feed ed stdin for each file hit:
find . -type f -exec bash -c 'printf "%s\n" ",s/old/new/g" w Q | ed -s '"'{}'" \;
sed is a Stream EDitor, not a file editor. Nevertheless, people everywhere tend to abuse it for trying to edit files. It doesn't edit files. GNU's sed (and some BSD sed's) have a -i option that makes a copy and replaces the original file with the copy. An expensive operation, but if you enjoy unportable code, I/O overhead and bad side effects (such as destroying symlinks), this would be an option:
sed -i 's/old/new/g' ./* # GNU sed -i '' 's/old/new/g' ./* # BSD for file in ./* # Other do [ -f "$file" ] && sed 's/old/new/g' "$file" > "$file~" && mv "$file~" "$file" done
Those of you who have perl 5 can accomplish the same thing using this code:
perl -pi -e 's/old/new/g' ./*
Recursively using find:
find . -type f -exec perl -pi -e 's/old/new/g' {} \;
If you want to delete lines instead of making substitutions:
# Deletes any line containing the perl regex foo perl -ni -e 'print unless /foo/' ./*
To replace for example all "unsigned" with "unsigned long", if it is not "unsigned int" or "unsigned long" ...:
find . -type f -exec perl -i.bak -pne \ 's/\bunsigned\b(?!\s+(int|short|long|char))/unsigned long/g' {} \;
Finally, for those of you with none of the useful things above, here's a script that may be useful:
#!/bin/sh # chtext - change text in several files # neither string may contain '|' unquoted old='olddomain\.com' new='newdomain\.com' # if no files were specified on the command line, use all files: [ $# -lt 1 ] && set -- ./* for file do [ -f "$file" ] || continue # do not process e.g. directories [ -r "$file" ] || continue # cannot read file - ignore it # Replace string, write output to temporary file. Terminate script in case of errors sed "s|$old|$new|g" -- "$file" > "$file"-new || exit # If the file has changed, overwrite original file. Otherwise remove copy if cmp -- "$file" "$file"-new >/dev/null 2>&1 then rm -- "$file"-new # file has not changed else mv -- "$file"-new "$file" # file has changed: overwrite original file fi done
If the code above is put into a script file (e.g. chtext), the resulting script can be used to change a text e.g. in all HTML files of the current and all subdirectories:
find . -type f -name '*.html' -exec chtext {} \;
Many optimizations are possible:
use another sed separator character than '|', e.g. ^A (ASCII 0x01)
the find command above could use either xargs or the built-in xargs of POSIX find
Note: set -- ./* in the code above is safe with respect to files whose names contain spaces. The expansion of ./* by set is the same as the expansion done by for, and filenames will be preserved properly as individual parameters, and not broken into words on whitespace.
A more sophisticated example of chtext is here: http://www.shelldorado.com/scripts/cmds/chtext