Differences between revisions 12 and 31 (spanning 19 versions)
Revision 12 as of 2008-11-10 00:09:37
Size: 408
Editor: 140
Comment: Hello, im from Space. We were all so sorry to hear about the unfortunate event and greatly concerned for your health and welfare smoker turkey cannon recipe More or less nothing seems worth bothering
Revision 31 as of 2011-02-18 01:26:50
Size: 4642
Editor: 190-36-145-91
Comment: consistency
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Hello, im from Space. We were all so sorry to hear about the unfortunate event and greatly concerned for your health and welfare smoker turkey cannon recipe More or less nothing seems worth bothering with. http://snaffel44.freeweb7.com/recipe8dc.html recipe for peppermint crunch truffeles oh, http://gentimo.blackwidowhosting.com/recipef66.html candied almond recipes, Well done.. <<Anchor(faq21)>>
== 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 [[UsingFind|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
Line 3: Line 124:
CategoryHomepage CategoryShell

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


CategoryShell

BashFAQ/021 (last edited 2022-11-03 23:42:27 by GreyCat)