Embed Perl script into Applescript?

Discussion in 'Mac Programming' started by VideoBeagle, Aug 8, 2015.

  1. VideoBeagle macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #1
    I have a couple of perl scripts. I would like to use Applescript to give them some UI features (one applescipt for each different perl script).

    they run from the command line, thus:
    Code:
    >:perl script.pl argument1 argument2.....argumentN
    the results of the script go into the folder you've navigated to in Terminal (so running it from a folder ~/Test/ puts the results in /Test/).

    I should be able to run them from an applescript, either by running a shell script or controlling terminal.

    WHAT I'D LIKE TO DO is instead of having the perl script reside as a file somewhere on my mac, I'd like for it to be embedded in the applescript itself. (so it's portable, can be easily moved to different machines, distributed, etc).

    I'm only at the beginning of my research on this idea but so far I'm finding mixed answers to whether this is possible from age old posts that aren't exactly what I'm asking.

    So, IS what I want to do possible to do?


    If the kind of perl script matters to the question, this is a simple sample of the kinds of scripts I'm using. If I can get this to work, the others will work.
    Code:
    #!/usr/bin/perl -w
    use strict;
    use warnings;
    use POSIX;
    use Data::Dumper;
    
    # Log activity to a file.
    sub Logger {
      my $file = shift;
      my $msg = shift;
      my $stop = shift || 0;
       
      open(my $LOG, ">>", $file) || die "Could not create log file.\n" . $!;
      #print $LOG sprintf("%s: %s\n", strftime("%H-%M-%S", localtime), $msg);  -Time Stamp in log files
      print $LOG sprintf("%s\n", $msg);
      close($LOG);
       
      # Exit with error
      unless ($stop == 0) { exit 1; }
    }
    
    # Globals
    my $logfile = strftime("%Y%m%d%H%M%S", localtime) . "_md.log";
    my $count = 1;
    
    # Loop through the given numbers
    OUTER: foreach my $number (@ARGV) {
       
      # Create a directory. Make sure it exists. Log an exit if you cannot.
      unless (-d $number) {
      mkdir($number);
      unless (-d $number) {
      Logger($logfile, sprintf("%s. Could not make directory",$count,$number));
      }
      }
       
      $count++;
    }
    
    # Log the end of the program
    # Logger($logfile, "Stopping");
    
    # exit without error
    exit 0;
    
     
  2. Mark FX macrumors regular

    Mark FX

    Joined:
    Nov 18, 2011
    Location:
    West Sussex, UK
    #2
    I have to confess that I know nothing about perl scripting, but what you describe should be possable with AppleScript, or AppleScriptObjC, using the "do shell script" command, or using the NSTask class in the OSX Cocoa Framework.
    To embbed your perl scripts into an AppleScript, you would have to create the AppleScript as a Cocoa AppleScript App, which can be done in Script Editor from the File > New from Template menu item, this will give you an App bundle which can contain various resources like your perl scripts, although you would be limited to the various basic AppleScript dialog type boxes for your UI elements.
    Alternativly you could create a Cocoa AppleScript project in Xcode, which would be my preference, as this would allow you to create a full featured custom interface, which would only be limited by your imagination.

    It would be helpful to know which version of OS X you are using, and also intending to run the App on, as AppleScript has undergone a lot of changes since 10.6 Snow Leopard, this includes various changes to the syntax, and also it's overall capabilities with the Cocoa Frameworks.
    So AppleScripts written in an earlier version of OS X are not guaranteed to run on later versions.

    Regards Mark
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    To bundle it all into a self-contained unit, I'd probably restructure it as an Automator Application. This will let you string together sequences of shell commands, AppleScript, GUI, etc. as separate Actions. The output of one will feed into the input of the next one.

    Perl will read stdin for its script if no command-file is present. You can make multiline input using repeated -e "one line cmd", but it will be much simpler to use the shell's ability to feed multiple lines to stdin. In has, it's called a "here document", and you can find that heading on the bash man page:
    https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/bash.1.html


    As to this question:
    the trick to that in Automator is to use the 'cd' command to position a Run Shell Script in the proper directory before executing other commands. For example, if you'd normally do this in a Terminal window:
    Code:
    perl something goes here
    
    and the input and output is intended to use the current working directory, then you'd put this in the Run Shell Script action:
    Code:
    cd ~/your/dir/goes/here
    perl something goes here
    
    In other words, provide the complete context (move to working directory, run command) in the shell commands.
     
  4. VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #4
    Sorry..I forgot it's not in my sig :oops:. 10.9.5

    Well, the plan is/how i invisioned it was to read in list of text, and then format the command: perl script.pl list1 list2....listn
    but the location of the script would be in the script itself, rather than the hardrive.

    The way I was envisionion it was more or less like the perl script would be a subroutine that's called.
    Automater would allow me to add in an Automater workflow that's used for post processing.


    So if the Automator or applescript has a "choose target folder" then you'd pass that location into the cd command?
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    If the target folder and the folder of inputs is the same, then that's how you'd do it. If they're different, then you might do it differently. You'd have to give details on exactly how your perl script works.


    I'm going to make a general suggestion here:
    Try something and see what happens.​

    That is, run some simple tests, and observe how things work. Doing simple tests to gain understanding is a very good way to learn things. Even if you don't get the result you expected, you've tried something concrete, and you can post what you tried and what happened. Worst case, you'll have learned what doesn't work.

    If you run into specific problems with what you've tried, post a question here, and tell us exactly what you tried (copy and paste commands), and what the exact output was (copy and paste error messages, etc.). Details are important.


    There are plenty of simple shell commands you can run as tests to see how 'cd' works. The simplest is probably 'pwd'. One step above that is 'pwd' with output redirection, e.g.:
    Code:
    pwd >~/my-test.txt
    There are also lots of simple commands that will operate on files in the current directory. For example, the 'file' command. Run it, pass it an argument, redirect output to a log file in ~/, and observe what happens.

    After several rounds of "try it and see what happens", you should be able to predict what will happen for a given command. That's a clear indication that you understand what's happening.
     
  6. VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #6
    [I was typing this while chown was typing the above, so it doesn't reflect what's in his comment]

    I'll keep some comments on my ongoing work on this for future reference...

    I've set up a simple automator workflow that asks for text, then runs the perl script to make a folder in the home folder.

    First issue is giving multiple text items (ie. Test1 Test2) produces a folder with name "Test1 Test2" rather than 2 folders named "Test1" and "Test2".

    Googling tells me that the argument that gets passed in to a variable $@, so you need to loop thru it to split it up like:
    Code:
    for f in "$@"
    do
    echo "$f"
    done
    
    Which won't work for Perl, so I'll need to a) figure out how to do it in perl and b) if that'll cause problems for how the real scripts work....
    The main problem I see with a loop causing the script to run once many times, rather that running many times at once is it would cause the error log function to make a bunch of one entry logs rather than a single log with a bunch of entries, which while not critical, would be less than desirable.
     
  7. VideoBeagle, Aug 8, 2015
    Last edited: Aug 8, 2015

    VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #7
    The perl script is in my first post under the spoiler button. It works exactly as I described.. it takes in a list of arguments and makes a subfolder for each argument in the folder it's called from.


    I've done some experiments...I'm right now having to figure out if it's possible to integrate the shell commands like cd with the perl script.

    [​IMG]

    --------------

    I'm thinking that Automator might not work for what i want. It seems to see everything as an individual programs it runs one after another, rather than a sequence of commands.

    So I'm back looking at embedding into Applescript itself.
     
  8. VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #8
    Moving back to using perl applescript, and it's a step forward, a step back.

    I have a script, it compiles, but when I run it gives a huge Error report.

    Code:
    do shell script "perl -e '
      #!/usr/bin/perl -w
    use strict;
    use warnings;
    use POSIX;
    use Data::Dumper;
    
    # Log activity to a file.
    sub Logger {
      my $file = shift;
      my $msg = shift;
      my $stop = shift || 0;
      
      open(my $LOG, \">>\", $file) || die \"Could not create log file.
    \" . $!;
      #print $LOG sprintf(\"%s: %s
    \", strftime(\"%H-%M-%S\", localtime), $msg);  -Time Stamp in log files
      print $LOG sprintf(\"%s
    \", $msg);
      close($LOG);
      
      # Exit with error
      unless ($stop == 0) { exit 1; }
    }
    
    # Globals
    my $logfile = strftime(\"%Y%m%d%H%M%S\", localtime) . \"_md.log\";
    my $count = 1;
    
    # Loop through the given numbers
    OUTER: foreach my $number (@ARGV) {
      
      # Create a directory. Make sure it exists. Log an exit if you cannot.
      unless (-d $number) {
      mkdir($number);
      unless (-d $number) {
      Logger($logfile, sprintf(\"%s. Could not make directory\",$count,$number));
      }
      }
      
      $count++;
    }
    
    # Log the end of the program
    # Logger($logfile, \"Stopping\");
    
    # exit without error
    exit 0;'
    tessy
    "
    

    And

    Code:
    error "Operator or semicolon missing before %H at -e line 17.
    Ambiguous use of % resolved as operator % at -e line 17.
    String found where operator expected at -e line 18, near \"print $LOG sprintf(\"\"
      (Might be a runaway multi-line \"\" string starting on line 17)
       (Missing semicolon on previous line?)
    String found where operator expected at -e line 27, near \"my $logfile = strftime(\"\"
      (Might be a runaway multi-line \"\" string starting on line 19)
       (Missing semicolon on previous line?)
    Having no space between pattern and following word is deprecated at -e line 27.
    Operator or semicolon missing before %H at -e line 27.
    Ambiguous use of % resolved as operator % at -e line 27.
    Operator or semicolon missing before %M at -e line 27.
    Ambiguous use of % resolved as operator % at -e line 27.
    Operator or semicolon missing before %S at -e line 27.
    Ambiguous use of % resolved as operator % at -e line 27.
    String found where operator expected at -e line 27, near \"S\", localtime) . \"\"
    Bareword found where operator expected at -e line 27, near \"\", localtime) . \"_md\"
       (Missing operator before _md?)
    String found where operator expected at -e line 45, at end of line
       (Missing semicolon on previous line?)
    Global symbol \"%M\" requires explicit package name at -e line 17.
    Global symbol \"%S\" requires explicit package name at -e line 17.
    syntax error at -e line 18, near \"print $LOG sprintf(\"\"
    Global symbol \"%s\" requires explicit package name at -e line 18.
    Global symbol \"%Y\" requires explicit package name at -e line 27.
    Global symbol \"%s\" requires explicit package name at -e line 37.
    Can't find string terminator '\"' anywhere before EOF at -e line 45.
    sh: line 48: tessy: command not found" number 127
    
    I've been poking at this on and off all day, so I'm putting it aside for now.
     
  9. cqexbesd macrumors regular

    Joined:
    Jun 4, 2009
    #9
    You can just chdir from within perl.
     
  10. Mark FX macrumors regular

    Mark FX

    Joined:
    Nov 18, 2011
    Location:
    West Sussex, UK
    #10
    Sorry for my slow reply.

    Very little changed with AppleScript from 10.9.5 Mavericks to Yosemite, so if you decided to go the AppleScript route, then forward compatibility should not be a problem.

    As stated previously, I'm sorry I can't help with any perl scripting issues, but I will watch your posting, and if you have any AppleScript specific issues, then I will assist if I'm able.

    Regards Mark
     
  11. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #11
    The first error (and several others) are because perl doesn't permit literal newlines in double-quoted strings. You've escaped the double-quotes, but you didn't escape the backslashes in your original posted code. Instead you just used a literal newline. If you want a newline in a double-quoted string, you should use the escape sequence \n.
    http://www.perlmonks.org/?node=String Literals in Perl

    Some of the other errors may just be a result of a parsing-error cascade from the first error near line 17.


    Here's one original line:
    Code:
      open(my $LOG, ">>", $file) || die "Could not create log file.\n" . $!;
    
    Here's the corresponding "line" that produces the error:
    Code:
      open(my $LOG, \">>\", $file) || die \"Could not create log file.
    \" . $!;
    
    Here's what you should have used:
    Code:
      open(my $LOG, \">>\", $file) || die \"Could not create log file.\\n\" . $!;
    
    Using an earlier suggestion, I suggest making an extremely simple perl program on which you can test the use of escaped double-quotes and escaped backslashes within an AppleScript string. I recommend a perl program that has two lines and prints two output lines. That is, each perl line should produce one line of output, and each output line should end with a newline. Run that and see if it works. If it doesn't, then post the code here.
     
  12. superscape macrumors 6502a

    superscape

    Joined:
    Feb 12, 2008
    Location:
    East Riding of Yorkshire, UK
    #12
    I'd totally take the approach Mark FX mentions, myself. Mashing AppleScript and perl together just seems odd to me, although I accept that you may have your reasons. Personally I'd see if I could go one step further and try to replicate the functionality of the perl script in Obj-C.
     
  13. VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #13
    Well, the thing is.. I don't know Obj-C, the OSX Cocoa Framework, etc. And I'm not about to learn them for this project.
    I'm using Perl because my friend who's helping me with this likes to use Perl (I'd prefer Python which I'm much more familiar with, but gift horse and all that.

    I had to take a break for a bit on this to work on other stuff, but I'm gonna get back to it this week.
     
  14. superscape macrumors 6502a

    superscape

    Joined:
    Feb 12, 2008
    Location:
    East Riding of Yorkshire, UK
    #14
    Fair enough!

    I do think that you're making life unnecessarily hard for yourself by shackling yourself to perl though. If you're determined to take that route then others have detailed the options above.

    However, I don't know much perl at all but from the look of it all you're doing is creating some folders based on user input. You don't need perl to do that, and perl seems a slightly strange choice to me given that you're already in AppleScript, from the sound of it.

    So why not forget perl and do it all in AppleScript from the start? Here's a very rough example of the sort of thing I mean:

    Code:
    --get some info from the user
    set theFolder to choose folder with prompt "Choose a folder to make your folder in"
    set theStringOfNumbers to text returned of (display dialog "Please enter a list of names for the folders. Separate the names with commas" default answer "folder_one,folder_two,folder_three")
    
    --make a name for the log file
    set theStartTime to do shell script "date \"+%H-%M-%S\""
    set theLogFile to (POSIX path of theFolder) & theStartTime & "_md.log"
    
    --split the comma separated list into an actual list
    set AppleScript's text item delimiters to ","
    set theFolderNameList to every text item of theStringOfNumbers
    set AppleScript's text item delimiters to ""
    
    
    --go through the list and make the folders
    repeat with theFolderName in theFolderNameList
        try
            --nb. mkdir -p makes the folder if it doesn't already exist. If it does exist then it fails silently.
            do shell script "mkdir -p " & quoted form of ((POSIX path of theFolder) & theFolderName)
            writeToLog("SUCCESS: Created file " & "(" & theFolderName & ")", theLogFile)
        on error
            writeToLog("ERROR: Failed to create file " & "(" & theFolderName & ")", theLogFile)
        end try
    end repeat
    
    
    
    --function to log errors to file
    on writeToLog(theText, theLogFile)
        --get the current time
        set theLogEntryTime to do shell script "date \"+%Y%m%d%H%M%S\" "
        do shell script ("echo " & quoted form of (theLogEntryTime & ": " & theText) & " >> " & quoted form of theLogFile)
    end writeToLog
    
    ...okay, so there's a smattering of shell script in there, but it's mostly native AppleScript. If you want much more of a user interface on it than that given by "display dialog" then odds are you're going to have to venture into Cocoa, one way or another.
     
  15. VideoBeagle thread starter macrumors 6502a

    Joined:
    Aug 17, 2010
    Location:
    App Q&A testing by request.
    #15
    That's not what the real scripts do...that is a sample that does the parts that interact with the mac.
    The full scripts takes a bunch of numbers, uses those numbers to make URL's, and fetch specific information from those pages and puts it in folders for a long simmering catalog project.

    The perl works fine to do what's needed. The parts that interface with the Mac are taking the numbers in, and creating the folders. If I can get this snipet to work, then the main scripts will work.

    My friend has been moving houses, so I haven't gotten back to this project for now.
     

Share This Page