Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Replace across files (extreme newbie)

by Anonymous Monk
on May 23, 2000 at 21:33 UTC ( #14411=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I wish to search for string x and replace it with string y for all the files in a directory and all its subdirectories.

thanks alot

Comment on Replace across files (extreme newbie)
Replies are listed 'Best First'.
RE: Replace across files (extreme newbie)
by mikfire (Deacon) on May 23, 2000 at 22:45 UTC
    For a minor tweak ( says the sysadmin, forever worried about users wasting his cycles ), try it this way
    find . -type f | xargs perl -pi.bak -e 's/string x/string y/g'
    Using the -exec flag will exec a perl for every file found. This can be slow and painful - the most expensive part of perl is the initial load. By piping to xargs, you start perl but once.

    If you you have too many files to fit on one command line, you can modify the command like this:

    find . -type f | xargs -n 255 perl -pi.bak -e 's/string x/string y/g'
    which will cause xargs to call perl with 255 files each time.

    Mik Firestone ( perlus bigotus maximus )

      As I find this find statement very handy but because I often
      forget commands I use seldom, I have put this useful thing
      into a useable script.
      Maybe a silly idea to execute a shell script invoking perl
      from a perlscript, but the target "Get it working" is reached :-)
      #!/usr/bin/perl -w # # replace_in_tree replaces stringx by stringy in files from current di +rectory # recursiveley and backups the original files to *.bak like # find . -type f | xargs -n 255 perl -pi.bak -e 's/string x/string y/g +' # does. # # Author : Sascha Wuestemann # License: Artistic GPL # eMail : use strict; sub usage { print <<EOF; usage: replace_in_tree <to_be_replaced> <replacement> <filematcher> replaces the string "to_be_replaced" with "replacement" from the current directory downwards the filetree in files matching filematcher, e.g. "*" or "*.html" example: replace_in_tree "Author: Bill Gates" "Author: Larry Wall" *.html EOF } sub exec_replace { open(GUN,"find . -name \"$ARGV[2]\" -type f | xargs -n 255 perl +k -e 's/$ARGV[0]/$ARGV[1]/g'|")|| die "can't execute shell commands:$ +!\n"; close(GUN)|| warn "couldn't close shell:$!\n"; } if (not $ARGV[2]) { usage; exit; } else { exec_replace } exit;

      there are no silly questions
        I think you are right: It's silly to execute this shell pipeline in a perl script.
        And the way you do it is even worse.

        If you want to put a complex shell command in a script, use a shell script, that's what it was made for.

        If you just want to execute a shell command in a perl script, use the system command. Unless you need the output of the command, then use backticks.

        Here is an example of a shell script doing the same as your perl script, but with much less resources:

        #!/bin/sh if [ $# -lt 3 ]; then echo " usage: replace_in_tree <to_be_replaced> <replacement> '<filematcher>' replaces the string "to_be_replaced" with "replacement" from the current directory downwards the filetree in files matching filematcher, e.g. '*' or '*.html' example: replace_in_tree 'Author: Bill Gates' 'Author: Larry Wall' '*.html' " exit fi find . -name "$3" -type f | xargs -n 255 perl -pi.bak -e 's/\Q$1\E/$2/ +g'
        Update: I forgot to mention that your script will probably fail.
        The third parameter (*.html) will be expanded by the shell, so perl will have the first filename in $ARGV2 and not the glob.
        Therefore I changed the usage to have the third parameter quoted.

Re: Replace across files (extreme newbie)
by ZZamboni (Curate) on May 23, 2000 at 21:41 UTC
    This might be easier to do with a combination of Perl and standard Unix commands:
    find . -type f -exec perl -pi.bak -e 's/x/y/g' {} \;
    The "." is the directory where the search will start, "-type f" is the condition for files that will be processed (this will process all regular files, you may want to narrow it to files with certain names, etc.), and the perl command will actually perform the replacement for each file, leaving the original in a file with the same name, but with termination ".bak". "x" and "y" are your search and replacement strings, and /g causes it to replace all ocurrences on every line.

    See the man page for the find command for more details on its options, and see the documentation for s for details on the substitution command itself.


Re: Replace across files (extreme newbie)
by plaid (Chaplain) on May 23, 2000 at 21:44 UTC
    If you're on a unix machine, the most fun way to do it would be with something like:
    find . -type f -exec perl -pi -e "s/string x/string y/g" {} \;
    This will find all files, starting in the current directory, and do the replacement intended. If you do not have access to the find command, you're probably going to want to look at File::Find, as it's the standard perl module to handle recursively finding files, and letting you perform some editting on each.
      The File::Find version would be something like this. perl -MFile::Find -pi -e 'BEGIN {find sub {-f && push @ARGV, $File::Find::name}, "."} s/string x/string y/g'
Re: Replace across files (extreme newbie)
by Anonymous Monk on May 24, 2000 at 17:52 UTC
    Thanks for all the good help.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://14411]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2016-02-13 21:39 GMT
Find Nodes?
    Voting Booth?

    How many photographs, souvenirs, artworks, trophies or other decorative objects are displayed in your home?

    Results (448 votes), past polls