Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is automatic editing of multiple do-files possible?

    Many of my do-files contain a file path that needs to be updated. Example:
    Current file path: O:\Prosjekter\...
    New file path: O:\Prosjekt\...
    Is there a way I can use Stata to loop over multiple do-files and edit them (in this case replace "O:\Prosjekter" with "O:\Prosjekt")?

    (I know how to obtain a list of the do-files and save the list in a macro).


  • #2
    Off-hand, my suggestion would be to not do this with Stata, but with other tools -- e.g. the editor VS Code has the ability to replace text in multiple files.

    This may be to do with my own lack of expertise, but the reason I suggest this is that reading strings from do-files and then writing them again into the same or new text files, will need you to be very careful with special character sequences, e.g. for macros, or for double quotes etc, and unless handled carefully, those will result in errors.

    If you still want to try with Stata code, you might want to do something along these lines:

    Code:
    local dofolder "<your folder>"
    local dofiles : dir `"`dofolder'"' files "*.do"
    local wrongpath "O:/Prosjekter/"
    local rightpath "O:/Proskect/"
    
    foreach dofile of local dofiles {
        tempname readfile writefile
        * open new file to write
        file open `writefile' using "`dofolder'/_`dofile'", write text replace // create new file with an underscore as a prefix
        * open file to read
        file open `readfile' using "`dofolder'/`dofile'", read text
        dis "Processing file `dofile' ..."
        file read `readfile' line
        local line_no = 0
        local changed = 0
        while r(eof) == 0 {
             local line_no = `line_no' + 1
             local fixed_line = usubinstr(`"`macval(line)'"', `"`wrongpath'"', `"`rightpath'"', .)
             if `"`macval(fixed_line)'"' != `"`macval(line)'"' {
                 dis "Changed line no. `line_no'."
                 local changed = 1
             }
             file write `writefile' `"`macval(fixed_line)'"' _newline
             file read `readfile' line
        }
        file close `readfile'
        file close `writefile'
        if !`changed' erase "`dofolder'/_`dofile'"
    }
    Note that I DO NOT warrant that the above code will not cause errors. It depends on the contents of your do-file with respect to characters such as quotes etc.

    Comment


    • #3
      Also, for the future, you might want to create a do-file with path and other project-specific declarations, and then include it in individual do-files. That way, if the path needs to be changed later, it will only need to be changed in that one file, not all of them.

      Comment


      • #4
        Alternatively, just set the path once in the main .do file, and use only relative paths after that
        ---------------------------------
        Maarten L. Buis
        University of Konstanz
        Department of history and sociology
        box 40
        78457 Konstanz
        Germany
        http://www.maartenbuis.nl
        ---------------------------------

        Comment


        • #5
          Thanks a lot. Very helpful!

          Will adopt single specification of root directory as from now!

          Comment


          • #6
            The under-used -fileread()- and -filewrite- functions can be used to read each do file's contents into a strL string, allowing the use of -subinstr()-. The following untested code, which could be shorter but less clear, illustrates what I have in mind:
            Code:
            clear
            local oldpath = ... whatever
            local newpath = ... new whatever
            local OriginalFileList = ....
            clear
            set obs 1
            gen strL contents = ""
            gen bites = .
            foreach file of local OriginalFileList {
               replace contents = fileread("`file'")
               replace contents = subinstr(contents, "`oldpath'", "`newpath'", .)
               local newname = "New" + "`file'"
               replace bites = filewrite("`newname'", contents)
            }

            Comment


            • #7
              I have been making minor edits to batches of do-files using code similar to Mike Lacy 's in #6 for a while. I'll just add a couple of subtle points.

              1. do-files may contain Unicode characters, so -usubinstr()- would be better than -subinstr()- unless you know for sure that you will not encounter any Unicode.

              2. If the number of files is very large, you can gain some efficiency by using Robert Picard's -filelist- program (available from SSC) rather than using -local ...: dir ...-, and, if you do that, wrap the write-out commands in a program and iterate it using Robert Picard's and my -runby- (also available from SSC). I should also add, though, that if the number of files is very large, are you sure that this one-size-fits-all editing is really appropriate for all of them? Just think twice before doing it, no matter which code you use for the purpose. If it is appropriate because it is a change of directory names, then I think the suggestions made by Hemanshu Kumar and Maarten Buis in #3 and #4 are a better approach than mass editing.

              Comment

              Working...
              X