Thursday, July 28, 2011

关于AppleScript一些记录

文中资料来源主要是《AppleScript跟我学》的中文译本以及Apple的的Technical Note

AppleScript是用在MacOSX上的脚本语言,和操作系统结合的相当紧密,不过一般来说我也没兴趣学一门应用不太广泛的专属语言,但一来是项目需要,而且这个语言确实好用并且简单易学,就当消遣消遣好了。写好的脚本可以用NSTask很方便的执行,而脚本本身几乎什么都能做。

编辑器可以选择自带的Apple Script Editor就在应用程序的实用工具下面,应付是绝对够了,需要代码提示的话请把偏好设置中的“使用脚本助理”勾选,而代码提示的输入是用Esc键,当然你也可以选择Xcode,它也可以创建AppleScript文件,代码提示什么的也比较习惯,不过本着轻松的态度,我还是使用自带的。

打开AppleScript编辑器,面板很简单,左上角是四个主要按键,录制,停止,运行,编译。录制什么的我还没用过,停止自然不用说,编译其实也无所谓,因为你在点运行的时候会先编译的。中间是编码框,当然是用来输入脚本的,最下面是描述与系统日志的输出,不做录制的话一般我们只需要关心系统日志的结果就行了。

既然是脚本语言嘛,语法什么的根本不要学,直接照着例子来,最简单的,让系统嗡鸣一下就是

Applescript代码 收藏代码
  1. beep

敲完了之后点一下运行就会执行了,编写代码的时候对于关键字等非引号内部的字符不需要在意大小写,反正运行和保存的时候会帮你格式化好。

你还可以发声,使用say命令,不过可能因为我的机器是黑苹果的缘故,始终报-241的错。

再复杂一点,清空废纸篓,这个时候你需要调用finder了

Applescript代码 收藏代码
  1. tell application "Finder"
  2. empty the trash
  3. end tell

这里tell application是呼唤程序,后续的的程序都必须写在引号里,中间包夹的自然是要这个程序做什么,最后end tell表示对这个程序调用结束。empty the trash是finder专属的操作,除了the之外其他都是背语法也没法实现的。实际上引号里的finder仍然不区分大小写。

如果对这段程序稍加修改

Applescript代码 收藏代码
  1. tell application "Finder"
  2. beep
  3. empty the trash
  4. end tell
  5. beep
  6. open the startup disk

很明显,tell到end tell中所有操作都默认主语是"Finder",不过对于系统功能,AppleScript还是能区分出其主语的,但如果把应用程序的操作写到了tell外面,那么就不知道如何进行了,当然根据脚本语言特性,第二个beep还是能执行的。而且编辑器的语法检测也还凑合,如果把empty the trash写在外面,直接语法关都过不去。

除了这些照书抄的标准外,项目中用到最多的其实还是其可以直接调用shell的特性,一句话就完事

Applescript代码 收藏代码
  1. do shell script "ps A"

毫无疑问shell本身也是去找应用程序的,但这么一来无疑轻松多了,有一点需要格外注意,AppleScript的默认shell是/bin/sh而我们用的终端则是/bin/bash,可能会出现在终端中可以执行,但脚本中却说找不到命令的情况,遗憾的是我并没有遇到过。

本段有待补全。。。。。。

令人惊喜的是,内置的编辑器还可以直接将脚本保存为脚本包或者应用程序,这确实方便了许多。保存为应用程序的时候首先腰进行语法检测,而一旦保存完毕之后,就可以将其添加到登录项(在系统偏好设置-帐户-登录项)里了,开机启动就是如此简单。保存的时候注意没有特别想法不要选中run-only这样会导致你的脚本文件无法编辑了。

内置编辑器令人惊奇的地方还不止这些,它还有和Mac上一贯优异的可操作性,比如你完全不必输入长长的application,只要在合适的地方写app,要么用代码提示的自动输入,甚至不用代码提示,在编辑器保存或者编译的时候,它会自动将其补全。除了关键字之外,即使是应用程序也可以方便的替换,比如写

Applescript代码 收藏代码
  1. tell application "shit"

这个shit如果计算机上找不到,那么在编译期会跳出窗口让你选择这个shit所代表的程序,并且替换,而且在后续的编写中,你一直使用shit的话,当再次编译时,不需要提示就会自动替换为刚才选择的程序,这个特性在下次打开编辑器的时候仍然保持。你还可以在代码编辑器按下ctrl健后按鼠标,就会出现整理好的很多快捷模块,自己选择就可以,更方便的是你可以先选中一段代码,然后在选贼快捷模块,那么选中的代码就会自动添加到快捷模块内部。

接下来是编程语言都有的变量,自然变量都是没有类型的,我猜。定义变量使用set,变量名规则与c类似

Applescript代码 收藏代码
  1. set pitcturPath to "/documents"

一旦定义,就可以在后续文本中使用该变量,而且该变量本身也可以修改,而且就代码看来更符合普通人的认识

Applescript代码 收藏代码
  1. set DDd to 100
  2. set DDd to "dajiahao"
  3. set aaa to DDd
  4. display dialog aaa
  5. display dialog DDd
  6. set DDd to 100
  7. display dialog aaa
  8. display dialog DDd

display dialog会用一个对话框显示输出,如果觉得比较烦人也可以直接看编辑器的结果区,不过它仅能显示最后一段代码的结果,即使是set语句也是有结果的,结果就是set的具体值,而且显示的时候还会特地区分字符串和数字,如果是字符串会加上引号。最特别的是如果你按下了对话框的取消按钮,脚本将不会继续执行下去。

+-*/^等操作符不必自然多说,但注意虽然变量无类型,但也不能对字符串进行加减乘除操作,如果需要拼接字符串使用&,可以用于字面值和变量指尖。字符串长度用the length of,字符串内部对引号的转移是常见的前面加反斜杠即\,自然的,你想在字符串里输入一个字面值的反斜杠就要连续输入两个反斜杠。和js类似,如果写"12"-3,那么会自动将"12"转为数值最后得到9.如果想要明确的转型,那么就要使用as

Applescript代码 收藏代码
  1. set abc to "12" as number
  2. set def to 12 as string

和现代脚本语言Python类似,AppleScript的list也非常简单,用大括号括起来就行了,比如{1,2,3,4},也类型也未必一致如{1,"2",3,4},直接用还是赋值都非常方便,下面是修改展示对话框的按钮的方法

Applescript代码 收藏代码
  1. set stringToBeDisplay "dajiahao"
  2. display dialog stringToBeDisplay buttons{"hello", "welcome", "goaway"} default button "hello"

default button后面可以不用字符串而直接用下标,不过可能它面向的编程人员不同,下标是以1开始的,所以这里得写成default button 3。set i to 100

list的连结也很方便,和字符串一样用&就可以了

Applescript代码 收藏代码
  1. set firstList to {1}
  2. set secondList to {2, 3}
  3. set thirdList to {4}
  4. set fourthList to firstList & secondList & thirdList

那么fourthList就是{1,2,3,4}

其他关于list的东西不想照抄了,这里还是先讲讲程序里如何调用这些脚本

首先我们可以用NSAppleScript类直接运行脚本,可以用源码也可以用文件形式,文件可以是源码形式也可以是编译好的。

脚本文件demo.applescript

Applescript代码 收藏代码
  1. set i to 100

根据之前的介绍,这里的脚本值就是100

Objective-c代码 收藏代码
  1. NSAppleEventDescriptor *eventDescriptor = nil;
  2. NSAppleScript *script = nil;
  3. NSBundle *bunlde = [NSBundle mainBundle];
  4. NSString *scriptPath = [bunlde pathForResource:@"demo"
  5. ofType:@"scpt"
  6. inDirectory:nil];
  7. if (scriptPath)
  8. {
  9. script = [[NSAppleScirpt alloc] initWithContentsOfURL:[NSURL fileURLWithPath:scriptPath] error:nil];
  10. if (script)
  11. {
  12. eventDescriptor = [script executeAndReturnError:nil];
  13. if (eventDescriptor)
  14. {
  15. NSLog(@"%@", [eventDescriptor stringValue]);
  16. }
  17. }
  18. }

没做任何校验,最低安全级别的代码,总之就是这样,如果返回的是list的话,则还要更加麻烦一点

Objective-c代码 收藏代码
  1. NSAppleEventDescriptor *e = [NSTask gtm_runAppleScript:@"value"];
  2. int count = [e numberOfItems];
  3. for (int i = 1; i <= count; i++)
  4. {
  5. NSAppleEventDescriptor *d = [e descriptorAtIndex:i];
  6. NSLog(@"%@", [d stringValue]);
  7. }

和之前提到的一样,下标以1开始。

若返回的是record形式的结果,那么就要比list再加一层,假设脚本是这样的

Applescript代码 收藏代码
  1. {key1:1, key2:2, key3:3}

所谓的加一层就是这个意思,第一次按下标获取描述符是record的个数,然后再对record里具体条目进行处理,item数值为条目数x2,前一个item是key,后一个item是对应的value

Applescript代码 收藏代码
  1. NSAppleEventDescriptor *e = [NSTask gtm_runAppleScript:@"value"];
  2. // 其实就返回一个record的话,count就是1
  3. int count = [e numberOfItems];
  4. for (int i = 1; i <= count; i++)
  5. {
  6. NSAppleEventDescriptor *d = [e descriptorAtIndex:i];
  7. // 这里会是6
  8. int items = [d numberOfItems];
  9. NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  10. for (int j = 1; j <= items; j +=2)
  11. {
  12. // 前一条是key的名字
  13. NSAppleEventDescriptor *dKey = [d descriptorAtIndex:j];
  14. NSString *key = [dKey stringValue];
  15. // 后一条是对应的值
  16. NSAppleEventDescriptor *dValue = [d descriptorAtIndex:j+1];
  17. NSString *value = [dValue stringValue];
  18. [dic setObject:value forKey:key];
  19. }
  20. }

如果根本不在意返回值,而是仅仅要执行一段脚本,也可以用NSTask,而且还有个问题就是如果想要给脚本传参数的话,之前提到的方法就只能靠自己修改脚本拼接了,想泛用的话还得改脚本,而且此时仅可使用NSTask,可以查阅到NSAppleScript根本没有运行时带参数的方法,假设文件为demo2.applescript

Applescript代码 收藏代码
  1. on run{para1, para2}
  2. log para1
  3. log para2
  4. end run

那么代码就要写成这样

Objective-c代码 收藏代码
  1. NSTask *task = nil;
  2. NSBundle *bunlde = [NSBundle mainBundle];
  3. NSString *scriptPath = [bunlde pathForResource:@"demo2"
  4. ofType:@"scpt"
  5. inDirectory:nil];
  6. if (scriptPath)
  7. {
  8. NSArray *a = [NSArray arrayWithObjects:@"fuck", @"you", nil];
  9. task = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/osascript"
  10. arguments:a];
  11. }
  12. return task;

所以我很头疼一点,如果又要有参数,又要有返回值那要如何处理。

AppleScript很方便的就是可以直接运行shell的源码或者shell文件,简单的使用do shell script即可,不过要注意sh文件的权限问题,否则会出现把sh文件在bash里运行ok,可AppleScript编辑器里却显示Permission denied的问题。

No comments:

Post a Comment