转自:http://bukkake.iteye.com/blog/828322
文中资料来源主要是《AppleScript跟我学》的中文译本以及Apple的的Technical Note
AppleScript是用在MacOSX上的脚本语言,和操作系统结合的相当紧密,不过一般来说我也没兴趣学一门应用不太广泛的专属语言,但一来是项目需要,而且这个语言确实好用并且简单易学,就当消遣消遣好了。写好的脚本可以用NSTask很方便的执行,而脚本本身几乎什么都能做。
编辑器可以选择自带的Apple Script Editor就在应用程序的实用工具下面,应付是绝对够了,需要代码提示的话请把偏好设置中的“使用脚本助理”勾选,而代码提示的输入是用Esc键,当然你也可以选择Xcode,它也可以创建AppleScript文件,代码提示什么的也比较习惯,不过本着轻松的态度,我还是使用自带的。
打开AppleScript编辑器,面板很简单,左上角是四个主要按键,录制,停止,运行,编译。录制什么的我还没用过,停止自然不用说,编译其实也无所谓,因为你在点运行的时候会先编译的。中间是编码框,当然是用来输入脚本的,最下面是描述与系统日志的输出,不做录制的话一般我们只需要关心系统日志的结果就行了。
既然是脚本语言嘛,语法什么的根本不要学,直接照着例子来,最简单的,让系统嗡鸣一下就是
- beep
敲完了之后点一下运行就会执行了,编写代码的时候对于关键字等非引号内部的字符不需要在意大小写,反正运行和保存的时候会帮你格式化好。
你还可以发声,使用say命令,不过可能因为我的机器是黑苹果的缘故,始终报-241的错。
再复杂一点,清空废纸篓,这个时候你需要调用finder了
- tell application "Finder"
- empty the trash
- end tell
这里tell application是呼唤程序,后续的的程序都必须写在引号里,中间包夹的自然是要这个程序做什么,最后end tell表示对这个程序调用结束。empty the trash是finder专属的操作,除了the之外其他都是背语法也没法实现的。实际上引号里的finder仍然不区分大小写。
如果对这段程序稍加修改
- tell application "Finder"
- beep
- empty the trash
- end tell
- beep
- open the startup disk
很明显,tell到end tell中所有操作都默认主语是"Finder",不过对于系统功能,AppleScript还是能区分出其主语的,但如果把应用程序的操作写到了tell外面,那么就不知道如何进行了,当然根据脚本语言特性,第二个beep还是能执行的。而且编辑器的语法检测也还凑合,如果把empty the trash写在外面,直接语法关都过不去。
除了这些照书抄的标准外,项目中用到最多的其实还是其可以直接调用shell的特性,一句话就完事
- do shell script "ps A"
毫无疑问shell本身也是去找应用程序的,但这么一来无疑轻松多了,有一点需要格外注意,AppleScript的默认shell是/bin/sh而我们用的终端则是/bin/bash,可能会出现在终端中可以执行,但脚本中却说找不到命令的情况,遗憾的是我并没有遇到过。
本段有待补全。。。。。。
令人惊喜的是,内置的编辑器还可以直接将脚本保存为脚本包或者应用程序,这确实方便了许多。保存为应用程序的时候首先腰进行语法检测,而一旦保存完毕之后,就可以将其添加到登录项(在系统偏好设置-帐户-登录项)里了,开机启动就是如此简单。保存的时候注意没有特别想法不要选中run-only这样会导致你的脚本文件无法编辑了。
内置编辑器令人惊奇的地方还不止这些,它还有和Mac上一贯优异的可操作性,比如你完全不必输入长长的application,只要在合适的地方写app,要么用代码提示的自动输入,甚至不用代码提示,在编辑器保存或者编译的时候,它会自动将其补全。除了关键字之外,即使是应用程序也可以方便的替换,比如写
- tell application "shit"
这个shit如果计算机上找不到,那么在编译期会跳出窗口让你选择这个shit所代表的程序,并且替换,而且在后续的编写中,你一直使用shit的话,当再次编译时,不需要提示就会自动替换为刚才选择的程序,这个特性在下次打开编辑器的时候仍然保持。你还可以在代码编辑器按下ctrl健后按鼠标,就会出现整理好的很多快捷模块,自己选择就可以,更方便的是你可以先选中一段代码,然后在选贼快捷模块,那么选中的代码就会自动添加到快捷模块内部。
接下来是编程语言都有的变量,自然变量都是没有类型的,我猜。定义变量使用set,变量名规则与c类似
- set pitcturPath to "/documents"
一旦定义,就可以在后续文本中使用该变量,而且该变量本身也可以修改,而且就代码看来更符合普通人的认识
- set DDd to 100
- set DDd to "dajiahao"
- set aaa to DDd
- display dialog aaa
- display dialog DDd
- set DDd to 100
- display dialog aaa
- display dialog DDd
display dialog会用一个对话框显示输出,如果觉得比较烦人也可以直接看编辑器的结果区,不过它仅能显示最后一段代码的结果,即使是set语句也是有结果的,结果就是set的具体值,而且显示的时候还会特地区分字符串和数字,如果是字符串会加上引号。最特别的是如果你按下了对话框的取消按钮,脚本将不会继续执行下去。
+-*/^等操作符不必自然多说,但注意虽然变量无类型,但也不能对字符串进行加减乘除操作,如果需要拼接字符串使用&,可以用于字面值和变量指尖。字符串长度用the length of,字符串内部对引号的转移是常见的前面加反斜杠即\,自然的,你想在字符串里输入一个字面值的反斜杠就要连续输入两个反斜杠。和js类似,如果写"12"-3,那么会自动将"12"转为数值最后得到9.如果想要明确的转型,那么就要使用as
- set abc to "12" as number
- set def to 12 as string
和现代脚本语言Python类似,AppleScript的list也非常简单,用大括号括起来就行了,比如{1,2,3,4},也类型也未必一致如{1,"2",3,4},直接用还是赋值都非常方便,下面是修改展示对话框的按钮的方法
- set stringToBeDisplay "dajiahao"
- display dialog stringToBeDisplay buttons{"hello", "welcome", "goaway"} default button "hello"
default button后面可以不用字符串而直接用下标,不过可能它面向的编程人员不同,下标是以1开始的,所以这里得写成default button 3。set i to 100
list的连结也很方便,和字符串一样用&就可以了
- set firstList to {1}
- set secondList to {2, 3}
- set thirdList to {4}
- set fourthList to firstList & secondList & thirdList
那么fourthList就是{1,2,3,4}
其他关于list的东西不想照抄了,这里还是先讲讲程序里如何调用这些脚本
首先我们可以用NSAppleScript类直接运行脚本,可以用源码也可以用文件形式,文件可以是源码形式也可以是编译好的。
脚本文件demo.applescript
- set i to 100
根据之前的介绍,这里的脚本值就是100
- NSAppleEventDescriptor *eventDescriptor = nil;
- NSAppleScript *script = nil;
- NSBundle *bunlde = [NSBundle mainBundle];
- NSString *scriptPath = [bunlde pathForResource:@"demo"
- ofType:@"scpt"
- inDirectory:nil];
- if (scriptPath)
- {
- script = [[NSAppleScirpt alloc] initWithContentsOfURL:[NSURL fileURLWithPath:scriptPath] error:nil];
- if (script)
- {
- eventDescriptor = [script executeAndReturnError:nil];
- if (eventDescriptor)
- {
- NSLog(@"%@", [eventDescriptor stringValue]);
- }
- }
- }
没做任何校验,最低安全级别的代码,总之就是这样,如果返回的是list的话,则还要更加麻烦一点
- NSAppleEventDescriptor *e = [NSTask gtm_runAppleScript:@"value"];
- int count = [e numberOfItems];
- for (int i = 1; i <= count; i++)
- {
- NSAppleEventDescriptor *d = [e descriptorAtIndex:i];
- NSLog(@"%@", [d stringValue]);
- }
和之前提到的一样,下标以1开始。
若返回的是record形式的结果,那么就要比list再加一层,假设脚本是这样的
- {key1:1, key2:2, key3:3}
所谓的加一层就是这个意思,第一次按下标获取描述符是record的个数,然后再对record里具体条目进行处理,item数值为条目数x2,前一个item是key,后一个item是对应的value
- NSAppleEventDescriptor *e = [NSTask gtm_runAppleScript:@"value"];
- // 其实就返回一个record的话,count就是1
- int count = [e numberOfItems];
- for (int i = 1; i <= count; i++)
- {
- NSAppleEventDescriptor *d = [e descriptorAtIndex:i];
- // 这里会是6条
- int items = [d numberOfItems];
- NSMutableDictionary *dic = [NSMutableDictionary dictionary];
- for (int j = 1; j <= items; j +=2)
- {
- // 前一条是key的名字
- NSAppleEventDescriptor *dKey = [d descriptorAtIndex:j];
- NSString *key = [dKey stringValue];
- // 后一条是对应的值
- NSAppleEventDescriptor *dValue = [d descriptorAtIndex:j+1];
- NSString *value = [dValue stringValue];
- [dic setObject:value forKey:key];
- }
- }
如果根本不在意返回值,而是仅仅要执行一段脚本,也可以用NSTask,而且还有个问题就是如果想要给脚本传参数的话,之前提到的方法就只能靠自己修改脚本拼接了,想泛用的话还得改脚本,而且此时仅可使用NSTask,可以查阅到NSAppleScript根本没有运行时带参数的方法,假设文件为demo2.applescript
- on run{para1, para2}
- log para1
- log para2
- end run
那么代码就要写成这样
- NSTask *task = nil;
- NSBundle *bunlde = [NSBundle mainBundle];
- NSString *scriptPath = [bunlde pathForResource:@"demo2"
- ofType:@"scpt"
- inDirectory:nil];
- if (scriptPath)
- {
- NSArray *a = [NSArray arrayWithObjects:@"fuck", @"you", nil];
- task = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/osascript"
- arguments:a];
- }
- return task;
所以我很头疼一点,如果又要有参数,又要有返回值那要如何处理。
AppleScript很方便的就是可以直接运行shell的源码或者shell文件,简单的使用do shell script即可,不过要注意sh文件的权限问题,否则会出现把sh文件在bash里运行ok,可AppleScript编辑器里却显示Permission denied的问题。