Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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
-- https://forums.macrumors.com/threads/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?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
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.
 

dylanryan

macrumors member
Aug 21, 2011
57
2
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.
 

kryten2

macrumors 65816
Mar 17, 2012
1,114
99
Belgium
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.
 

Attachments

  • Picture 11.png
    Picture 11.png
    227.4 KB · Views: 505

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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.
 
Last edited:

kryten2

macrumors 65816
Mar 17, 2012
1,114
99
Belgium
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.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
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.
 

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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.

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

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.

It works
 

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
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
 

kryten2

macrumors 65816
Mar 17, 2012
1,114
99
Belgium
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 ..
 

Attachments

  • Screen Shot 2013-07-06 at 04.29.06.png
    Screen Shot 2013-07-06 at 04.29.06.png
    247.4 KB · Views: 298
  • Screen Shot 2013-07-06 at 05.00.12.png
    Screen Shot 2013-07-06 at 05.00.12.png
    258.5 KB · Views: 282
Last edited:

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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
 
Last edited:

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
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.

View attachment 421860

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
 

kryten2

macrumors 65816
Mar 17, 2012
1,114
99
Belgium
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.

Here's what I'm looking at:

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

Attachments

  • Picture 1.png
    Picture 1.png
    44.7 KB · Views: 228

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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

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.
 

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
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.

Sorry to hear you couldn't get it working.

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.

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.
 

kryten2

macrumors 65816
Mar 17, 2012
1,114
99
Belgium
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.
 

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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
 
Last edited:

chown33

Moderator
Staff member
Aug 9, 2009
10,747
8,420
A sea of green
and just for completeness: uploadSOrev again:

I don't know why you say "again", since I don't see it posted at all previously on this thread.

EDIT: that made no sense.
I agree. But maybe not for the same reasons as you.

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)
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.
 

dancks

macrumors regular
Original poster
Nov 8, 2009
100
0
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.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.