Why won't this applescript execute as folder action script?

Discussion in 'Mac Programming' started by dancks, Jul 1, 2013.

  1. macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #1
    If you haven't tuned in so far: I am trying to make a smart folder that uploads files to and creates directories at /var/www/media/ on my server whenever a file or directory is saved to the smart folder. I am using an applescript as a folder action to run a shell script. The shell script has been briefly tested, and appears to work flawlessly.

    Here is my applescript:

    Code:
    -- https://discussions.apple.com/thread/1485146?start=0&tstart=0
    -- https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/osascript.1.html
    -- http://forums.macrumors.com/showthread.php?t=1089834
    on adding folder items to this_folder after receiving added_items
    	set FolderPath to POSIX path of added_items
    	set the_name to the name of added_items
    	set isfile to kind of added_items
    	tell application "System Events"
    		try
    			if isfile = "file" then
    				do shell script "HD:users:Jason Dancks:Desktop:place_on_server:uploadSOrev" & "F" & FolderPath & the_name
    			else
    				do shell script "HD:users:Jason Dancks:Desktop:place_on_server:uploadSOrev" & "DIR" & FolderPath
    				set FolderPath to path to added_items --from POSIX to mac path
    				attach action to (alias FolderPath) using "HD:users:Jason Dancks:Desktop:place_on_server:alertscriptrev.scpt"
    			end if
    		end try
    		display dialog FolderPath
    	end tell
    end adding folder items to
    
    the finder window does this delayed blink, like its trying to execute the script. It appears to fail silently
    the display dialog part doesn't even run. Why? How would I even debug this?
     
  2. macrumors 603

    Joined:
    Aug 9, 2009
    #2
    First, your added_items object is a list of aliases, not an alias itself. As a result, this won't work, at a minimum:
    Code:
    	set FolderPath to POSIX path of added_items
    
    Other things may also not work because added_items is a list. You must iterate over the items of the list using a loop. The simplest list-iterating loop in AppleScript is:
    Code:
    repeat with each_thing in list_of_things
      statements
    end
    
    Reference:
    https://developer.apple.com/library...gguide/reference/ASLR_control_statements.html

    If you intended to write this_folder as the object whose Posix path is being retrieved, then that's a mistake.

    For reference:
    https://developer.apple.com/library...tlangguide/reference/ASLR_folder_actions.html
    Syntax
    Code:
    on adding folder items to alias after receiving listOfAlias
     [ statement ]...
    end [ adding folder items to ]
    


    For debugging, or more like "debugging by printf", you can use the 'say' command. Example:
    Code:
    on adding folder items to this_folder after receiving added_items
    	set FolderPath to POSIX path of this_folder
    	say "Folder path is: " & FolderPath
    

    I also recommend that you use successive enhancement for writing the folder-action handler.

    That is, start with an empty handler. Add one statement. Confirm it's what you expect, possibly by using the say command. You could also use "display dialog" and pass it the string.

    Then add the next statement. Confirm it's what you expect. If at any point, the say command says something that's not what you expect, stop and fix it before continuing.
     
  3. macrumors member

    Joined:
    Aug 21, 2011
    #3
    Don't know why the display dialog isn't working, but in apple script, using & to append strings doesn't add spaces, which I suspect you are expecting. Right now, you will try to execute, for example, "/[long path]/uploadSOrevF/Some/Path/HereSomeName", whereas I expect you want "/[long path]/uploadSOrev F /Some/Path/Here SomeName". So, you should add some spaces to see if that at least helps it do the right shell script.

    Also, although it probably won't matter if you have a case-insensitive (default) file system, the folder is typically "Users", not "users". If you do have a case-sensitive file system, that might be another problem.

    Third, do shell script doesn't seem to like colon-separated paths, from brief testing (could be wrong). You probably want to use /Users/Jason Dancks/Desktop/place_on_server/uploadSOrev as your path. (You may also need to escape the space in your user name, I'm not 100% sure).

    An on error block in your try might help you catch any other errors.
     
  4. macrumors 6502a

    Joined:
    Mar 17, 2012
    Location:
    Belgium
    #4
    You can also test your folder action by commenting out the folder action handler and provide the input yourself. The log command can be useful as well.
     

    Attached Files:

  5. dancks, Jul 5, 2013
    Last edited: Jul 5, 2013

    thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #5
    Sorry for the long delay

    I've been working 12 hour days lately and sometimes the last thing I want to do is sit down at a computer, so sorry for the long delay.

    anyways, like chown said, I tried to break things down to the simplest pieces and build upwards. I try a really simple folder action script to verify its even being run:

    Code:
    on adding folder items to this_folder after receiving added_items
    	say "Good"
    end adding folder items to
    nothing. I mean, finder does that delayed blink but nothing else. So I have a starting point.
     
  6. macrumors 6502a

    Joined:
    Mar 17, 2012
    Location:
    Belgium
    #6
    Create another user and try your simple folder action when logged on as that user. Try to disable/enable Folder Actions in the Folder Actions Setup window and check the Console application --> Console Messages for any errors.

    1. What version of OS X are you using?
    2. Check if you have the following two files in your user Library-->LaunchAgents folder. The files com.apple.FolderActions.enabled.plist and com.apple.FolderActions.folders.plist

    Post the contents of those two files.
     
  7. macrumors 603

    Joined:
    Aug 9, 2009
    #7
    Make sure the 'say' command works.

    Type this into AppleScript Editor, and run it:
    Code:
    say "Working"
    
    If it says "Working", then the command should work elsewhere. If it says nothing, post again.
     
  8. thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #8
    ok:
    Code:
    me:LaunchAgents me$ ls -al
    total 16
    drwxr-xr-x   4 root  wheel   136 Oct 18  2012 .
    drwxrwxr-t+ 59 root  admin  2006 Nov 19  2012 ..
    -rw-r--r--   1 root  wheel   612 Aug 27  2012 com.adobe.AAM.Updater-1.0.plist
    -rw-r--r--   1 root  wheel   788 Jul 10  2012 com.google.keystone.agent.plist
    I know its there somewhere from console:

    Code:
    7/5/13 6:32:50 AM	[0x0-0x19019].com.apple.systemevents[184]	com.apple.FolderActions.enabled: Already loaded
    It works
     
  9. macrumors 603

    Joined:
    Aug 9, 2009
    #9
    Next, confirm that a Folder Action script from another source works.

    1. Launch Automator.app, tell it to create a Folder Action.
    2. Attach the workflow to a different folder than the one you used originally.
    3. Add a "Run Shell Script" action to the workflow.
    4. Set the "Pass input" popup to "as arguments"
    5. Observe: the default shell script changes.
    6. Change the "echo $f" inside the loop to "say $f".
    7. Save the workflow.
    8. Add a file to the target folder.
    9. Does it say the pathname of the added item?

    Also, try kryten2's suggestion of using another user. Make a temporary one if you have to.

    When logged in as the new user, make sure Folder Actions are turned on.
    https://discussions.apple.com/thread/4784552?start=0&tstart=0
     
  10. kryten2, Jul 5, 2013
    Last edited: Jul 5, 2013

    macrumors 6502a

    Joined:
    Mar 17, 2012
    Location:
    Belgium
    #10
    I made a simple folder action script on Mountain Lion and attached it to a folder. After enabling Folder Actions in the Folder Actions Setup window, a LaunchAgents folder was created containing the two files I mentioned in my previous post(see attached thumbnails).


    Note : Files also present on Snow Leopard.

    Edit : I just noticed from your ls -al output that you probably didn't look in the right folder.

    Code:
    betelgeuse:~ kryten$ ls -al Library/LaunchAgents/
    total 16
    drwxr-xr-x   4 kryten  staff   136  6 jul 04:29 .
    drwx------@ 47 kryten  staff  1598  6 jul 04:21 ..
    -rw-r--r--   1 kryten  staff   425  6 jul 04:26 com.apple.FolderActions.enabled.plist
    -rw-r--r--   1 kryten  staff   592  6 jul 04:29 com.apple.FolderActions.folders.plist
    betelgeuse:~ kryten$ ls -al /Library/LaunchAgents/
    total 0
    drwxr-xr-x   2 root  wheel    68 15 nov  2011 .
    drwxr-xr-x+ 60 root  wheel  2040 24 mrt 18:25 ..
     

    Attached Files:

  11. thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #11
    I haven't tested this on another account yet.

    chown: I made that workflow in Automator. It worked.

    kryten2: You were right about looking in the wrong folder.

    Here's what I'm looking at:

    View attachment 421690

    View attachment 421691

    View attachment 421692

    I want to say my problem was attaching a script vs a workflow (not that I know the difference). IDK.
     
  12. dancks, Jul 7, 2013
    Last edited: Jul 7, 2013

    thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #12
    my current theory is that this part is never being read or always reads "false" (or its equivalent):

    Code:
    on adding folder items to this_folder after receiving added_items
    question is how and why.

    EDIT: I tried using automator to run my script as a folder action, so I wrapped my AppleScript in an Automator workflow. And that didn't work either. My next step is to remake the script functionality with only what automator gives me.

    Screen shot 2013-07-07 at 6.37.32 AM.png
     
  13. macrumors 603

    Joined:
    Aug 9, 2009
    #13
    You should use the AppleScript prototype code that Automator gives you. That's the handler that's expected, and if you only provide a different one, then nothing happens. Providing an uncalled-for handler in AppleScript is similar to writing a function in C (or shell), but never calling it.

    If you're familiar with the concept of a callback, then Automator is looking for a specific callback, that is, a specific event-handler. If you don't provide a handler for that specific event, then nothing happens when the event occurs.

    Again, I advise starting with the simplest case and expanding. Example:
    Code:
    on run {input, parameters}
    	
    	say "Input: " & (input as string)
    	say "Parameters: " & (parameters as string)
    	
    	return input
    end run
     
  14. macrumors 6502a

    Joined:
    Mar 17, 2012
    Location:
    Belgium
    #14
    When using Automator you don't need the folder action handler in your Run Applescript action eg :

    Code:
    on adding folder items to this_folder after receiving these_items
    -- Statement
    end adding folder items to
    
    Also notice that in the examples given here and in your other thread applescript differentiate file from folder, I've put my statements to get the name and kind properties of items in a tell application "Finder" or tell application "System Events" block. Yours will give an error.

    Code:
    	set the_name to the name of each_thing
    	set isfile to kind of each_thing
    Do shell script doesn't need a tell application "System Events" block either.

    This is what I get when trying to view your attachments.
     

    Attached Files:

  15. thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #15
    I just tested this. It runs in automator debug environment but not on its own attached as a folder action. I am only familiar with callback from a very brief encounter with javascript. I assume it works the same as using a function pointer in C? It looks kind of generic, like it takes a variable number of arguments, like its meant to be overwritten or inherited.

    @Kryten: OK I should've caught that. I'll have to change it tomorrow after the grind.
     
  16. thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #16
    Ok, so I couldn't get it to work. I even player around with a strictly automator and shell script example but it wouldn't fit my parameters, specifically the problem of how do I apply the same functionality to sub folders.

    I think my next step is to create a daemon in C. I think I'll create a environment variable it will simply poll that will contain the path of a changed folder. But then have an automator folder action script to detect any changes int that folder.

    I could simply poll the folders... yeah I'll do that.
     
  17. macrumors 603

    Joined:
    Aug 9, 2009
    #17
    Sorry to hear you couldn't get it working.

    You don't have to write a daemon. launchd already has a key for that: QueueDirectories.

    Google search terms:
    launchd watch folder

    Interesting result:
    http://hints.macworld.com/article.php?story=20080423051638134

    search terms:
    launchd QueueDirectories

    Top result, explains queue directory:
    http://developer.apple.com/library/...stemStartup/Chapters/CreatingLaunchdJobs.html

    Online man page explaining all launchd plist keys:
    https://developer.apple.com/library.../Reference/ManPages/man5/launchd.plist.5.html


    Since your goal is to copy files, you will need some logic in your daemon that waits for writes to a file to complete. Otherwise the copy will copy an incomplete file.

    Also, I'm unsure what order metadata is written, relative to file contents. I assume after. Since metadata includes resource-fork & xattrs (refer to man page for 'xattr' cmd), you might want to delay for a few seconds after the data-copy appears to complete.
     
  18. macrumors 6502a

    Joined:
    Mar 17, 2012
    Location:
    Belgium
    #18
    I'm also sorry to hear you couldn't get it working. I see chown33 has already given the excellent advise to use launchd instead. Keep us informed of your progress and good luck.
     
  19. dancks, Jul 17, 2013
    Last edited: Jul 17, 2013

    thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #19
    the launchd plist looks very promising. Thank you for telling me about it. I don't think I'll look at applescript again it looks that good. (Not really, I just won't use it for triggered actions or "folder actions").

    My plan is to run this simple shell script every hour: check:

    Code:
    #!/bin/bash
    cd $1
    list=$(ls)
    check=$(ff="$1/list.txt"; cat $ff)
    notfound=true
    for i in $check
    do
    	notfound=true
    	for j in $list
    	do
    		if [[ "$j" = "$i" ]]
    		then
    			notfound=false
    			break
    		fi
    	done
    	if [[ $notfound ]]
    	then
    		path="$(pwd)/$j"
    		if [[ -d $i ]]
    		then
    			/users/Jason/Desktop/place_on_server/uploadSOrev "DIR" $path
    			/users/Jason/Desktop/place_on_server/revised $path
    			cd $path
    		else
    			/users/Jason/Desktop/place_on_server/uploadSOrev "F" $path
    		fi
    	fi
    done
    ls | cat > list.txt
    exit 0
    
    and just for completeness: uploadSOrev again:

    Code:
    #http://stackoverflow.com/questions/17204092/bash-script-trouble-interpretting-input/17204840?noredirect=1#17204840
    #!/bin/bash
    addr=$(ifconfig -a | /users/Jason/Desktop/place_on_server/check_ip)
    relpath=$(/users/Jason/Desktop/place_on_server/strip /users/Jason/Desktop/place_on_server/ $2)
    if [[ $1 = "DIR" ]]
    then
      shift
      #( IFS=/; echo ssh "root@$addr" mkdir -p "/var/www/media/$*"; )
      #( IFS=/; ssh "root@$addr" "mkdir -p /var/www/media/$*"; "chmod 755 /var/www/media/$*";)
      #( ssh "root@$addr" "mkdir -p /var/www/media/$relpath"; "chmod 755 /var/www/media/$relpath"; )
      ( ssh "root@$addr" "cd /var/www/media; mkdir -p $relpath/; chmod 755 $relpath/;" )
    elif [[ $1 = "F" ]]
    then
      shift
      last=$#
      file=${!last}
      #( IFS=/; echo rsync "$file" "root@$addr:/var/www/media/$*" )
      #( IFS=/; chmod 755 "$*"; rsync "$file" "root@$addr:/var/www/media/$*"; )
      ( IFS=/; chmod 755 "$*"; rsync "$file" "root@$addr:/var/www/media/$relpath"; )
    else
      echo "Unknown command '$1'"
    fi
    
    the plan here is that the first script is the first script to be run and uses uploadSOrev when appropriate, by checking the list of files and directories listed in list.txt found in every directory and sub-directory compared to ls.

    I plan on have this run in hour intervals as opposed to the folder action idea I had originally. maybe I'll shorten it in case I need more current update. I'll play with it. Anyways the trouble I'm running into is its testing for files in a subdirectory that are in fact in the parent directory. I think I have an issue with the (not quite) recursiveness in check. (So for example, its looking for /users/me/Desktop/place_on_server/sub/a.txt which is actually in /users/me/Desktop/place_on_server/a.txt).

    EDIT: that made no sense.
    here's the output:

    Code:
    Jason$ ./revised /users/Jason/Desktop/place_on_server
    chmod: /users/Jason/Desktop/place_on_server/SO/a.out: No such file or directory
    rsync: link_stat "/users/Jason/Desktop/place_on_server/SO/a.out" failed: No such file or directory (2)
    rsync error: some files could not be transferred (code 23) at /SourceCache/rsync/rsync-40/rsync/main.c(992) [sender=2.6.9]
    chmod: /users/Jason/Desktop/place_on_server/SO/alertscript.scpt: No such file or directory
    rsync: link_stat "/users/Jason/Desktop/place_on_server/SO/alertscript.scpt" failed: No such file or directory (2)
    rsync error: some files could not be transferred (code 23) at /SourceCache/rsync/rsync-40/rsync/main.c(992) [sender=2.6.9]
    chmod: /users/Jason/Desktop/place_on_server/SO/alertscriptrev.scpt: No such file or directory
     
  20. macrumors 603

    Joined:
    Aug 9, 2009
    #20
    I don't know why you say "again", since I don't see it posted at all previously on this thread.

    I agree. But maybe not for the same reasons as you.

    You're showing the output from a ./revised command, which is presumably a shell script you wrote. Unfortunately, you haven't posted the contents of ./revised.

    Your ./revised script is apparently using chmod and rsync, but that's just surmise from the error messages. Anything else, only you know.


    Please follow this template:
    1. I wrote the following code:
    Code:
    Post code here
    
    2. It should do the following:
    1. Describe step by step what it should do.
    2. Use numbered steps if necessary.
    3. I expected the result to be:
    Describe expected result here, including expected output.

    4. Instead, the actual result was:
    Describe actual result here, including actual output.

    Without some description of what you expect to happen, no one knows what should happen. Yes, we know you're trying to copy files to a server. It's how the copy should happen that needs to be described.

    You have uncommented code, possibly containing some errors in semantics or concept, but since no one knows what it should do, no one knows whether what it actually does is correct, or where it's incorrect, what the correct thing is.

    Shell syntax is not like most other languages. Many things are allowed, and will do something, but whether that's what is intended or not is a different question. So it's not at all clear whether what actually happens is what you want to happen, or whether something else should happen but you don't know how to express it correctly.


    I honestly can't tell what you expect your "hourly" shell script to do, by which I mean the script that begins:
    Code:
    #!/bin/bash
    cd $1
    list=$(ls)
    
    It certainly has some interesting uses of commands, such as this:
    Code:
    ls | cat > list.txt
    
    which is more succinctly expressed as:
    Code:
    ls >list.txt
    
    I'm also unclear on how you expect certain expressions to evaluate. For example:
    Code:
    	if [[ $notfound ]]
    	then
    
    The 'then ... fi' block will execute for both values you assign to notfound, i.e.:
    Code:
    notfound=true
    notfound=false
    
    will produce the same result: the block is executed. If that's what you intended, fine, but that then makes me wonder why two values are needed.

    If you're not familiar with bash scripting, I wouldn't expect you to know all the details of [[ expr ]] evaluation. But if you're attempting to write a bash script, I would expect you to know bash well enough to write the script, or at least be referring to the bash man page while writing the script.

    It appears to me that you're not testing any of the smaller sub-parts of the script at all. You seem to just be writing the whole thing, running it, and hoping for it to work. Unfortunately, hope alone is a remarkably ineffective debugging strategy.

    If you're not already familiar with how bash conditionals work, then you really need to write several different simplified bash scripts, which you can test and then learn what actually happens. Doing so would show exactly what's needed for notfound.

    You should learn about the -x option to bash, which causes it to show you the commands being executed. Refer to the bash man page, find the 'set' command description, and look at the -x option description.


    You should also make test scripts for things like this:
    Code:
      ( IFS=/; chmod 755 "$*"; rsync "$file" "root@$addr:/var/www/media/$relpath"; )
    
    to make sure that what you expect to happen is actually happening, and there are no strange side effects.


    I don't really understand what your general approach is at all. That is, you haven't explained why you have such substantial scripts (uploadSOrev, revised) with lots of directory traversal and pathname building.

    You seem to be using 'rsync' to perform the copying, but you also have a bunch of shell-based "find the file, see if it already exists on the server" logic, and that logic doesn't seem to be working. In short, the shell scripting itself appears to be a significant part of the overall problem.

    What I'm wondering is why you have any "see if it already exists on the server" logic at all.

    I'm pretty sure that rsync is already capable of traversing directory trees and copying things that don't exist at the target location. In other words, if you just gave rsync the base of the directory tree to copy, the target server location, and the correct set of options, it would be able to make the target server directory be an exact duplicate of the source directory. If that's not what you want to happen, then I don't understand what you're trying to accomplish.

    There is also a command (find) that is specifically designed to traverse directory trees, evaluate files and directories for a large number of possible conditions, and then either print the path of the file, or run a command with the file.

    I don't know if 'find' would be more or less suitable than rsync for doing your traversals, because I don't understand why you have the traversals in the first place. Again, this comes back to a lack of explanation about how you're trying to copy, and exactly what you want the result on the server to be.


    At minimum, I suggest breaking the problem down into much smaller shell scripts, which you can individually test for correctness. You should be able to run each of your conditional blocks individually (DIR and F) and then manually confirm it did the correct thing.

    If you don't have reliable building-blocks, then anything built with them is itself unreliable.


    Rather than writing traversal scripts that actually run rsync, write scripts that echo out the commands that should be run, along with all the correct arguments. That way you can do some debugging without needing rsync to run correctly. You can also setup "list.txt" files manually, or target files on the server (also manually), and confirm that what happens with any "copy if it doesn't exist on server" logic is behaving properly.

    In short, make the traversal observable, then observe it.
     
  21. dancks, Jul 18, 2013
    Last edited: Jul 18, 2013

    thread starter macrumors member

    dancks

    Joined:
    Nov 8, 2009
    #21
    fixed:

    Code:
    #!/bin/bash
    cd $1
    list=$(ls)
    check=$(ff="$1/list.txt"; cat $ff)
    notfound=true
    for i in $check
    do
    	notfound=true
    	for j in $list
    	do
    		if [[ "$j" = "$i" ]]
    		then
    			notfound=false
    			break
    		fi
    	done
    	if [[ $notfound ]]
    	then
    		path="$(pwd)/$j"
    		if [[ -d $i ]]
    		then
    			/users/Jason/Desktop/place_on_server/uploadSOrev "DIR" $path
    			/users/Jason/Desktop/place_on_server/revised $path
    		else
    			/users/Jason/Desktop/place_on_server/uploadSOrev "F" $path
    		fi
    	fi
    done
    ls | cat > list.txt
    cd $1
    exit 0
    YAY!!!!!

    Next project is to build my computer and try to install hackintosh.

    @chown: I apologize for the way I post sometimes. Usually its last minute. Not an excuse. Also, I tend to post code but say its saved under a different name as I have a tendency to name things "firsttry" "3rdmethodrev2" etc which only have relevance to me and I fear that calling it such would cause confusion. check = revised in this case. sorry.
     

Share This Page