Hi
This isn't strictly a GPC question, but it is related, so I'll ask anyway :|
Is there an easy way to change a string in all source files to another string?
For example, if I want to change all occurrences of "Type foo = Object (bar)" to "Type Tfoo = Class (Tbar)" in all ".pas" and ".pp" files in a directory tree, is there an easy way to do it, using standard GNU tools? I could of course write a program to do it, but I figured there could be an easier way. Thanks.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/
At 10:29 +0000 8/3/06, Prof A Olowofoyeku (The African Chief) wrote:
For example, if I want to change all occurrences of "Type foo = Object (bar)" to "Type Tfoo = Class (Tbar)" in all ".pas" and ".pp" files in a directory tree, is there an easy way to do it, using standard GNU tools? I could of course write a program to do it, but I figured there could be an easier way. Thanks.
There are many ways to do it. I do this regularly using BBEdit and it's trivial, but you can do it using perl -i -p -e 's/whatever/whateverelse/', in combination with either zsh's ** to get all directories, or find -exec.
find . -name "*.pas" -or -name "*.pp" -exec perl -i -p -e 's/Type (\w+) = Object ((\w+))/Type T\1 = Class (T\2)/' '{}' ';'
find . -name "*.pas" -or -name "*.pp" -print
will list all the .pas or .pp files
find . -name "*.pas" -or -name "*.pp" -exec /bin/echo '{}' ';'
will execute teh command between -exec and ';', substituting the filename for '{}'.
perl -i -p -e 'perl code' filename
will execute the perl code for each line in filename, doing whatever it says, and then automatically printing the result, and then replace the file without making a backup.
Or something like that, I'd hate to try to get all the quoting exactly right as it passes through the shell, find, possibly another shell, and then grep...
And be weary of any version control files getting caught up by the find (SVN files add extensions, so they should be ok in this case).
Heaven help you if you don't have good backups.
I am really not responsible for the consequences of such a replace!
Enjoy, Peter.
On Wed, Mar 08, 2006 at 06:54:50PM +0800, Peter N Lewis wrote:
At 10:29 +0000 8/3/06, Prof A Olowofoyeku (The African Chief) wrote:
For example, if I want to change all occurrences of "Type foo = Object (bar)" to "Type Tfoo = Class (Tbar)" in all ".pas" and ".pp" files in a directory tree, is there an easy way to do it, using standard GNU tools? I could of course write a program to do it, but I figured there could be an easier way. Thanks.
There are many ways to do it. I do this regularly using BBEdit and it's trivial, but you can do it using perl -i -p -e 's/whatever/whateverelse/', in combination with either zsh's ** to get all directories, or find -exec.
find . -name "*.pas" -or -name "*.pp" -exec perl -i -p -e 's/Type (\w+) = Object ((\w+))/Type T\1 = Class (T\2)/' '{}' ';'
find . -name "*.pas" -or -name "*.pp" -print
will list all the .pas or .pp files
find . -name "*.pas" -or -name "*.pp" -exec /bin/echo '{}' ';'
will execute teh command between -exec and ';', substituting the filename for '{}'.
perl -i -p -e 'perl code' filename
will execute the perl code for each line in filename, doing whatever it says, and then automatically printing the result, and then replace the file without making a backup.
Or something like that, I'd hate to try to get all the quoting exactly right as it passes through the shell, find, possibly another shell, and then grep...
And be weary of any version control files getting caught up by the find (SVN files add extensions, so they should be ok in this case).
Heaven help you if you don't have good backups.
I don't know about the heaven thing, but perl can do backups on the fly:
perl -i.old -p -e 'perl code' filename
Emil
I am really not responsible for the consequences of such a replace!
Enjoy, Peter.
Prof A Olowofoyeku (The African Chief) wrote:
This isn't strictly a GPC question, but it is related, so I'll ask anyway :|
Is there an easy way to change a string in all source files to another string?
For example, if I want to change all occurrences of "Type foo = Object (bar)" to "Type Tfoo = Class (Tbar)" in all ".pas" and ".pp" files in a directory tree, is there an easy way to do it, using standard GNU tools? I could of course write a program to do it, but I figured there could be an easier way. Thanks.
sed can do it, though only within single lines. E.g.:
sed 's/Type (.*) = Object ((.*))/Type T\1 = Class (T\2)/' input.pas > output.pas
The general syntax for sed reaplacement command is 's/search/replace/'. There can be several replacements, separated with ;. If you want to allow several replacements within one line, add a g after the last / (it doesn't do any harm do always do this, unless you specifically want to replace only the first occurence per line).
In fact, you can use any character instead of /, e.g. 's|foo|bar|' (useful if the expressions contain / themselves).
The syntax of the replacement string is a (basic) "regular expression" (can be found on the net in many places). In particular, . means any character, * means any number of the previous (thus .* means anything), and ( ... ) is grouping, while ( and ) are literal parentheses (both occur in the example above). In the replacement string, the content of ( ... ) groups can be substituted with \1, \2, ... in sequential order.
The exact replacement to use can vary depending on how flexible you want it. E.g., if `Type' doesn't have to be there in the same line, you can omit it from both search and replace strings. (sed does replacements within lines, so it wouldn still work when `Type' is there.) OTOH, if you want to require `Type' to be at the beginning of the line, you could write s/^Type .../Type .../ (^ means beginning of line, but only in the search string -- in the replacement it's not necessary to state it again).
sed is case-sensitive. You can get a limited amount of case-insensitivity by using character sets, which are enclosed in [ ... ], e.g. [Oo]bject to match both Object and object.
Of course, sed doesn't know about Pascal syntax, so it will happily replace in strings, comments, and anything that fits the pattern, so use with care.
Note that (as shown in the usage above) sed doesn't replace the file in place, but writes to standard output. To replace several files in place, you can make a (ba)sh script like this:
#!/bin/sh for f do sed 's/foo/bar/' "$f" > "$f.new" && mv "$f.new" "$f" done
(Put in the sed command you want, of course.)
Then you call it with the file names you want, e.g.
./myscript *.pas
or
./myscript `find -name "*.pas" -o -name "*.pp"`
(using the find program to find all such files in the tree).
The latter only works as long as the file names contain no spaces. Otherwise:
find -name "*.pas" -o -name "*.pp" -print0 | xargs -0 ./myscript
Of course, when replacing files, you'd better make sure your sed expression is correct and/or backup your files first.
A somewhat more elaborate version of the script, that will show the differences and ask the user whether to replace:
#!/bin/sh for f do sed 's/foo/bar/' "$f" > "$f.new" && if diff -u "$f" "$f.new"; then rm "$f.new" # sed made no change else mv -i "$f.new" "$f" fi done
If you want to do it interactively, many editors offer regular expresions, e.g. my editor PENG can do basic and extended regex replacements (the latter just have a slightly different syntax, usually requiring fewer \es), and optionally ask for confirmation for every single replacement. End of plug. ;-)
Frank
If you're on Unix/Linux/OS X et al, you could use sed, e.g.
more file | sed 's|Type foo = Object (bar)|Type Tfoo = Class (Tbar)|' > newfile
You'll need to nest this in a loop.
In csh it'd be something like:
foreach file (*.pas) ... end
I think in bash you'd be after:
for file in *.pas do ... done
Hope this is enough to get you started -- good luck.
I have my own small script to deal with filename expansion which avoids shell limits on the number of parameters, etc., which is why I forget exactly how the filename expansion loop things work.
Grant
Hi
This isn't strictly a GPC question, but it is related, so I'll ask anyway :|
Is there an easy way to change a string in all source files to another string?
For example, if I want to change all occurrences of "Type foo = Object (bar)" to "Type Tfoo = Class (Tbar)" in all ".pas" and ".pp" files in a directory tree, is there an easy way to do it, using standard GNU tools? I could of course write a program to do it, but I figured there could be an easier way. Thanks.
Best regards, The Chief
Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/
Grant Jacobs wrote:
If you're on Unix/Linux/OS X et al, you could use sed, e.g.
more file | sed 's|Type foo = Object (bar)|Type Tfoo = Class (Tbar)|' > newfile
BTW, more is a pager. It will usually just write out the file when used in a pipe, but that's kind of a misuse. Usually one would use cat instead, or input redirection (sed < file), or simply an argument (for sed, as sed accepts input file name arguments, i.e. sed 's...' file).
You'll need to nest this in a loop.
In csh it'd be something like:
foreach file (*.pas) ... end
I think in bash you'd be after:
for file in *.pas do ... done
Hope this is enough to get you started -- good luck.
I have my own small script to deal with filename expansion which avoids shell limits on the number of parameters, etc., which is why I forget exactly how the filename expansion loop things work.
Looks right. BTW, if size limitations are a concern (i.e., rather many files), then the find | xargs combination is often useful, as I wrote in my other mail. xargs will group the arguments up to the limits the system can handle and call the program (in my case, the script) as often as necessary.
Frank
Hi
Thanks all for the input. This all looks very hairy to me! I will give it a go however. I only need to get it right once, and then I will have my definitive "search and replace" script.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.greatchief.plus.com/