Previous 1 2 Next 16 Replies Latest reply: Apr 21, 2015 2:58 AM by willy3oy
RedTruck Level 4 Level 4 (2,145 points)
How can I make an app, with Automator, Apple Script, or some developer tool that will let me find and replace multiple items within a single document.

Here is what I need in a nutshell:

I create on-demand content for a local cable television station. I have to create individual XML files for metadata for each episode. I have made a template of the XML file, with key words in the fields that need to change. There are about twenty of them. The rest of the XML file stays the same.

Right now, I have to do a find and replace for each keyword, one at a time. (BTW, I have to use either Dashcode or Text Edit for this...)

I would like to have some kind of form or multiple find and replace feature so I can type all of my FIND fields paired with all of my REPLACE fields, then press return and have the entire XML file update.

I hope this makes sense. Any help would be appreciated.

MacG5 1.8Dual - 2GB RAM, MacBook Pro 2.4 - 4GB RAM, Mac OS X (10.5.2), FCP Studio1+2, DVC Pro25+50, AJA I/oHD, Z1U, FibreJet, Dell 2407 & 3007
  • red_menace Level 6 Level 6 (14,785 points)
    Using lists and find/replace isn't that great with Automator, and it's dialogs don't even allow multiple line entries. I have a few AppleScript handlers that do most of your requirements (the multiple item edit dialog doesn't use find/replace pairs, but can use the find item as an item to edit), so I assembled a few of them into a script that should get you started. The following script reads the chosen file, prompts for the find terms (place your terms in the list as needed), puts up a dialog to edit the selected terms, does a find/replace, and finally makes a new file on the desktop with the result in it:

    <pre style="
    font-family: Monaco, 'Courier New', Courier, monospace;
    font-size: 10px;
    margin: 0px;
    padding: 5px;
    border: 1px solid #000000;
    width: 720px; height: 340px;
    color: #000000;
    background-color: #FFDDFF;
    overflow: auto;"
    title="this text can be pasted into the Script Editor">
    -- search and replace multiple items

    on run
    set TheFIle to choose file -- the original text file
    set TheFolder to (path to desktop) -- the folder for the output file
    set TheName to (GetUniqueName for TheFIle from TheFolder) -- the name for the output file
    set TheText to read TheFIle -- get the text to edit

    set Originals to {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"} -- the terms that can be replaced
    set Originals to (choose from list Originals default items Originals with prompt "Select the terms to replace:" with multiple selections allowed) -- the specific terms to replace

    set Replacements to (EditItems of Originals with Title given Prompt:"Edit the following replacement terms:") -- the replacement terms

    repeat with AnItem from 1 to count Originals
    set TheText to (ReplaceText of TheText from (item AnItem of Originals) to (item AnItem of Replacements))
    end repeat

    try -- write a new output file
    tell application "Finder" to make new file at TheFolder with properties {name:TheName}
    set OpenFile to open for access (result as alias) with write permission
    write TheText to OpenFile starting at eof
    close access OpenFile
    on error errmess
    try
    log errmess
    close access OpenFile
    end try
    end try

    end run


    to GetUniqueName for SomeFile from SomeFolder
    (*
    check if SomeFile exists in SomeFolder, creating a new unique name if needed
    parameters - SomeFile [mixed]: a source file path
    SomeFolder [mixed]: a folder to check
    returns [text]: a unique file name and extension
    *)
    set {Counter, Divider} to {"00", "_"}

    -- get the name and extension
    set {name:TheName, name extension:TheExtension} to info for file (SomeFile as text)
    if TheExtension is missing value then set TheExtension to ""
    set TheName to text 1 thru -((count TheExtension) + 2) of TheName

    set NewName to TheName & "." & TheExtension
    tell application "System Events" to tell (get name of files of folder (SomeFolder as text))
    repeat while it contains NewName
    set Counter to text 2 thru -1 of ((100 + Counter + 1) as text) -- leading zero
    set NewName to TheName & Divider & Counter & "." & TheExtension
    end repeat
    end tell

    return NewName
    end GetUniqueName


    to EditItems of SomeItems given Title:TheTitle, Prompt:ThePrompt
    (*
    displays a dialog for multiple item edit (note that a return is used between each edit item)
      for each of the items in SomeItems, a line containing it's text is placed in the edit box
        the number of items returned are padded or truncated to match the number of items in SomeItems
    parameters - SomeItems [list]: a list of text items to edit
    TheTitle [boolean/text]: use a default or the given dialog title
    ThePrompt [boolean/text]: use a default or the given prompt text
    returns [list]: a list of the edited items, or {} if error
    *)
    set {TheItems, TheInput, TheCount} to {{}, {}, (count SomeItems)}
    if TheCount is less than 1 then return {} -- error

    if ThePrompt is in {true, false} then -- "with" or "without" Prompt
    if ThePrompt then
    set ThePrompt to "Edit the following items:" & return -- default
    else
    set ThePrompt to ""
    end if
    else -- fix up the given prompt a little
    set ThePrompt to ThePrompt & return
    end if

    if TheTitle is in {true, false} then if TheTitle then -- "with" or "without" Title
    set TheTitle to "Multiple Edit Dialog" -- default
    else
    set TheTitle to ""
    end if

    set {TempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
    set {SomeItems, AppleScript's text item delimiters} to {SomeItems as text, TempTID}

    set TheInput to paragraphs of text returned of (display dialog ThePrompt with title TheTitle default answer SomeItems)

    repeat with AnItem from 1 to TheCount -- pad/truncate entered items
    try
    set the end of TheItems to (item AnItem of TheInput)
    on error
    set the end of TheItems to ""
    end try
    end repeat
    return TheItems
    end EditItems


    to ReplaceText of SomeText from OldItem to NewItem
    (*
    replace all occurances of OldItem with NewItem
    parameters - SomeText [text]: the text containing the item(s) to change
    OldItem [text]: the item to be replaced
    NewItem [text]: the item to replace with
    returns [text]: the text with the item(s) replaced
    *)
    set SomeText to SomeText as Unicode text -- TID's are case insensitive with Unicode text
    set {TempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, OldItem}
    set {ItemList, AppleScript's text item delimiters} to {text items of SomeText, NewItem}
    set {SomeText, AppleScript's text item delimiters} to {ItemList as text, TempTID}
    return SomeText
    end ReplaceText
    </pre>

    HTH
  • RedTruck Level 4 Level 4 (2,145 points)
    Holy cow, that was a lot of work! Thanks for your efforts. I will check it out on Tuesday at work and get back to you. I found a bunch of apps for the PC, but I don't want to add Parallels to this workflow, it'll only slow me down. But if what you did works you have no idea (or maybe you do...) how much time you saved me.

    Thanks again.

    Matt
  • red_menace Level 6 Level 6 (14,785 points)
    I wasn't that much work, since I already had the handlers in my library. Most of the time was spent testing to make sure it worked correctly. Just note that the edit dialog uses returns after each entry, so all you need to do is put in your search terms, then edit the items in the dialog. I also have an entry dialog which might have been a little easier in that the items needed are in the prompt, but the dialog is limited in size so all that will fit is about 15 entries. Let me know how it turns out or if it needs some tweaking.
  • RedTruck Level 4 Level 4 (2,145 points)
    quick question... I copied your script into a text editor. What program do I have to use to run this as an app.

    Pardon me, I am just learning about coding so I don't know where to start here.
  • RedTruck Level 4 Level 4 (2,145 points)
    Awesome. Just figured it out. "Get you started" was what I didn't read the first time. I just edited your script in AppleScript and it worked like a charm. Thank you so much. You saved me a ton of time.

    Post back and I will give you a green star.
  • red_menace Level 6 Level 6 (14,785 points)
    Glad it worked for you. There are a lot of AppleScript resources at MacScripter, but there isn't quite as much available for Automator for some reason (and most of that is aimed at developing actions) - there is some information (between the developer speak) from Apple's developer site:

    http://developer.apple.com/macosx/automator.html
    http://developer.apple.com/documentation/AppleApplications/Conceptual/AutomatorC oncepts/Automator.html
  • RedTruck Level 4 Level 4 (2,145 points)
    I knew Automator would not be the solution, but there was no other forum that looked like I would get an answer in. I figured if someone was using Automator to the point where they could offer solutions, they (you) probably knew a bit about scripting as well. Thanks for the help.
  • RedTruck Level 4 Level 4 (2,145 points)
    Red Menace,

    You helped me so much when you sent me that script. I was wondering if you could help me just one more time. (I have started to learn objective-c and cocoa by the way, thanks for the tip.) I have the script referencing a file with an XML extension, and it is case sensitive. When it outputs the file to the desktop the extension is lower case and I have to change it every time. Seeing as I make several hundred of these at a time, it takes a while to change the case. Is there a way to have it output the file with the extension all upper case? I tried to find the place/places in the file where I might change the code, but none of my attempts have worked. Would be much obliged.
  • red_menace Level 6 Level 6 (14,785 points)
    Hello again

    The GetUniqueName handler is the only place that the extension for the new file is broken out, so you can use a case-changing handler on the variable TheExtension there. I'm posting a general-purpose handler that changes case - you can add a call to it in the GetUniqueName handler like so:

          -- after the statement:
    if TheExtension is missing value then set TheExtension to ""
          -- add:
    set TheExtension to (ChangeCase of TheExtension to "upper")

    <pre style="
    font-family: Monaco, 'Courier New', Courier, monospace;
    font-size: 10px;
    margin: 0px;
    padding: 5px;
    border: 1px solid #000000;
    width: 720px;
    color: #000000;
    background-color: #FFEE80;
    overflow: auto;"
    title="this text can be pasted into the Script Editor">
    to ChangeCase of SomeText to CaseType
    (*
    changes the case or capitalization of SomeText to the specified CaseType using Python
    parameters - SomeText [text]: the text to change
    CaseType [text]: the type of case desired:
    "upper" = all uppercase text
    "lower" = all lowercase text
    "title" = uppercase character at start of each word, otherwise lowercase
    "capitalize" = capitalize the first character of the text, otherwise lowercase
    returns [text]: the changed text 
    *)

    set SomeText to SomeText as text
    if CaseType is not in {"upper", "lower", "title", "capitalize"} then return SomeText

    return (do shell script "/usr/bin/python -c \"import sys; print unicode(sys.argv[1], 'utf8')." & CaseType & "().encode('utf8')\" " & quoted form of SomeText)
    end ChangeCase
    </pre>
  • RedTruck Level 4 Level 4 (2,145 points)
    ONCE AGAIN...! You have saved me a lot of time, which is very valuable in what I do. Thank you so much.
  • gamebreakers Level 1 Level 1 (0 points)
    I am trying to create a watch folder that uses your script to:

    1. Have a folder that receives multiple xml files that run the script one by one.
    2. then move the files to an output folder.
    3. remove the original from the watch folder.

    I tried modifying the set TheFIle to choose file -- the original text file to:

    with multiple selections allowed

    But that doesn't seem to work. I know i'm missing a step. Any help is much appreciated!

    Thanks!
  • red_menace Level 6 Level 6 (14,785 points)
    To handle multiple files you will need to repeat through each item in a list (for example, move the items in the run handler to a separate one and add statements to the folder action handler to repeat through the items sent to it). You shouldn't have to choose the file items with a dialog - the folder action will be providing them.

    To help keep track of your specific modifications, it might be a good idea to post a new topic with your setup and what you have done so far.
  • PeterBreis0807 Level 7 Level 7 (32,945 points)

    Use Renamer.app

     

    Peter

  • asharke1 Level 1 Level 1 (0 points)

    Hi i know this is an old thread, but I'm looking to replace 3 items in an .xml file class utf-8

     

    I've tried using red_menace's code but am having difficulty with getting it to work

     

    what i need to achieve is two things

    1) entering a variable "gNumber" and then adding that as a prefix to two terms "thumbs/" and "images/"

    2) replacing "_blank" with "_self"

     

    i don't need to create a unique file, just want to replace the original

     

    ---

     

    on run

        set theFile to choose file -- the original text file

        set TheText to read theFile -- get the text to edit

        set gNumber to text returned of (display dialog "What Gallery #?" default answer "")

        set Originals to {"_blank", "images/", "thumbs/"} -- the terms that can be replaced

       

        set Replacements to {"_self", gNumber & "images/", gNumber & "thumbs/"}

       

        repeat with AnItem from 1 to count Originals

            set TheText to (ReplaceText of TheText from (item AnItem of Originals) to (item AnItem of Replacements))

        end repeat

       

        try -- write a new output file

            tell application "Finder" to make new file at TheFolder with properties {name:TheName}

            set OpenFile to open for access (result as alias) with write permission

            write TheText to OpenFile starting at eof

            close access OpenFile

        on error errmess

            try

                log errmess

                close access OpenFile

            end try

        end try

       

    end run

     

     

    to EditItems of SomeItems given Title:TheTitle, Prompt:ThePrompt

        (*

    displays a dialog for multiple item edit (note that a return is used between each edit item)

      for each of the items in SomeItems, a line containing it's text is placed in the edit box

        the number of items returned are padded or truncated to match the number of items in SomeItems

    parameters - SomeItems [list]: a list of text items to edit

    TheTitle [boolean/text]: use a default or the given dialog title

    ThePrompt [boolean/text]: use a default or the given prompt text

    returns [list]: a list of the edited items, or {} if error

    *)

        set {TheItems, TheInput, TheCount} to {{}, {}, (count SomeItems)}

        if TheCount is less than 1 then return {} -- error

       

        if ThePrompt is in {true, false} then -- "with" or "without" Prompt

            if ThePrompt then

                set ThePrompt to "Edit the following items:" & return -- default

            else

                set ThePrompt to ""

            end if

        else -- fix up the given prompt a little

            set ThePrompt to ThePrompt & return

        end if

       

        if TheTitle is in {true, false} then if TheTitle then -- "with" or "without" Title

            set TheTitle to "Multiple Edit Dialog" -- default

        else

            set TheTitle to ""

        end if

       

        set {TempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}

        set {SomeItems, AppleScript's text item delimiters} to {SomeItems as text, TempTID}

       

        set TheInput to paragraphs of text returned of (display dialog ThePrompt with title TheTitle default answer SomeItems)

       

        repeat with AnItem from 1 to TheCount -- pad/truncate entered items

            try

                set the end of TheItems to (item AnItem of TheInput)

            on error

                set the end of TheItems to ""

            end try

        end repeat

        return TheItems

    end EditItems

     

     

    to ReplaceText of SomeText from OldItem to NewItem

        (*

    replace all occurances of OldItem with NewItem

    parameters - SomeText [text]: the text containing the item(s) to change

    OldItem [text]: the item to be replaced

    NewItem [text]: the item to replace with

    returns [text]: the text with the item(s) replaced

    *)

        set SomeText to SomeText as Unicode text -- TID's are case insensitive with Unicode text

        set {TempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, OldItem}

        set {ItemList, AppleScript's text item delimiters} to {text items of SomeText, NewItem}

        set {SomeText, AppleScript's text item delimiters} to {ItemList as text, TempTID}

        return SomeText

    end ReplaceText

Previous 1 2 Next