ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AppleScript基础入门:语法与数据类型全解析

AppleScript基础入门:语法与数据类型全解析 写AppleScript这类脚本语言最大的尴尬往往是它太老、太“边缘”教程大半都是十几年前写的。但你如果正在用macOS又经常做重复性文件操作、需要控制某个应用、或者想给自己的工作流加一点自动化那你大概率会绕回到AppleScript面前。这套macOS自带的脚本语言语法松散、资料陈旧但它在系统自动化这个领域几乎是无可替代的存在。这篇文章是AppleScript基础系列的第一篇我会把基本语法和数据类型一次讲透语句结构、注释规则、变量赋值、运算符以及字符串、数字、布尔、列表、记录、日期、文件和别名这几种核心类型。适合完全没有AppleScript经验的macOS用户也适合想系统复习一遍的老手。1. 为什么要学AppleScript先搞清它的定位1.1 AppleScript解决了什么问题我用macOS快十年最深的感受是系统里很多繁琐操作其实就差一个“自己跟自己对话”的接口。AppleScript就是苹果给的这层接口——它是一种运行在macOS上的脚本语言用来向应用发送指令、读写文件、控制系统设置甚至驱动UI上的按钮。你不需要打开Xcode不需要编译只要写一段类似英文的脚本就能让Finder、邮件、音乐、Safari这些应用“听话”。举个最简单的场景每天上班要下载一批报表手动把PDF文件按日期归档到不同文件夹还要在邮件里提取某个附件。如果纯手动操作五分钟起步。用AppleScript写一个小脚本双击或者定时触发十秒内全部完成。这就是AppleScript的核心价值它把“跨应用的重复操作”变成了一段可复用的命令。之前整理照片时我写过一段脚本按拍摄日期批量移动到对应月份文件夹再自动重命名。整个过程不到40行跑完后我算了算至少省了我一个下午。这也是为什么即使在Python、JavaScript非常成熟的今天我仍然建议macOS用户花点时间学AppleScript——很多系统Shell和Python脚本做起来很费劲的UI控制、应用交互AppleScript几句话就能搞定。1.2 典型适用场景与不适合的场景基于自己的使用经验我列几个AppleScript真正擅长的场景批量文件操作批量重命名、按规则移动/复制、读取文件信息、生成归档清单。控制自带应用给“邮件”写草稿、把“提醒事项”批量加条目、用“音乐”播放指定歌单。配合UI自动化当应用没有提供苹果事件支持时可以用System Events模拟键盘/鼠标点击这一步是很多自动化工具做不到的。与其他工具衔接在osascript命令行里调用嵌入Shell脚本、快捷指令Shortcuts或Automator工作流。定时任务配合launchd或者日历提醒实现定时提醒、定时备份等后台任务。反过来AppleScript不适合做的事儿也很明确不适合做复杂数据处理和大规模计算。列表和记录的操作能支撑一些简单逻辑但跟Python比字符串正则、JSON解析、数组操作都太吃力了。你非要用AppleScript写爬虫或数据分析那是拿扳手钉钉子能干但想骂人。我的经验是AppleScript负责“控制macOS和应用”Python/Shell负责“数据处理”两者通过osascript和标准输入输出打通效率最高。2. 运行环境脚本编辑器与osascript命令行2.1 脚本编辑器最简单的起点macOS自带一个写AppleScript的工具叫“脚本编辑器”Script Editor在“应用程序-实用工具”里面也可以用Spotlight直接搜“Script Editor”。打开后就是一个带菜单栏的文本编辑器顶部有“运行”“编译”“录制”三个按钮。写代码时这个编辑器的逻辑和普通编辑器不太一样你输入文本后要先点“编译”按钮让系统检查语法编译通过后代码里的关键字会自动变成彩色、还有一些缩进和连字符的转化。然后再点“运行”真正执行。所以你在这个编辑器里看到的高亮代码其实是已经被系统“格式化”过的。我建议新手一开始就用脚本编辑器原因是它可以帮你纠错。AppleScript对语法非常敏感比如字符串引号写错他会直接标红而且编辑器内置“语法高亮”和“变量跟踪”比在命令行里瞎写直观很多。等你熟悉了语法再切换到命令行也不迟。2.2 命令行osascript如果脚本已经调试稳定我基本都是通过命令行跑。打开终端一句话就能执行osascript -e display dialog hello也可以把脚本存成.scpt或者.applescript文件然后用osascript /path/to/your/script.scpt.scpt是编译后的二进制格式.applescript是明文源码二选一都行我一般存.applescript方便用Git管理版本和查看改动。如果你希望双击就运行也可以把脚本导出为.app应用文件在脚本编辑器里“文件-导出-应用程序”即可。2.3 编译与运行到底区别在哪AppleScript的“编译”Compile过程很有意思。它不只是检查语法错误还会做一些文本标准化。比如你输入{}他会自动改成列表字面量的样式输入中文引号它会帮你转成英文标准引号。所以脚本编辑器里看到的内容往往和你原始输入有一点点格式差异这是正常的。运行就是真正执行编译后的指令。AppleScript是解释执行没有独立的“字节码”概念但编译后的代码在创建.scpt时会被缓存所以跑起来速度也还能接受。对初学者来说记住一句话先编译再运行编译不报错基本语法就没大问题。3. 基本语法语句、注释、变量与运算符3.1 语句以句号结束AppleScript最独特的语法习惯是每条语句以句号英文的.结尾而不是分号或换行。例如display dialog Hello set myName to 张三写不写句号其实都能编译过但官方规范推荐写句号目的是让代码更像“自然语言段落”。这和Python用缩进、C用分号是两种思维。我自己的习惯是每个完整的动作指令后面加一个句号阅读时能明显感觉到“一句指令结束了”在长脚本里梳理逻辑很舒服。整段代码的块结构也有讲究。AppleScript没有花括号而是用“关键字对”来包裹块典型结构是if xxx then -- 代码 end if这种“开头一个关键字、结尾对应end xxx”的风格贯穿了条件、循环、函数和tell块后面例子会反复出现。3.2 注释单行与块注释AppleScript支持两种注释足够覆盖日常需求-- 这是单行注释双横线开头 (* 这是块注释 可以跨多行 *)块注释在你要临时屏蔽一大段代码时特别好用。AppleScript的注释没有像Python docstring那样成为语言特性但作为脚本维护者我强烈建议在每个代码块上方用单行注释说明“这段干什么”几个月后回来看代码你会感谢当时的自己。3.3 变量赋值set与copy的区别AppleScript声明变量很简单不用指定类型直接赋值即可set theName to 张三 set theAge to 30 set piValue to 3.14 set isOk to true我在这里要专门讲一个容易踩坑的点set和copy都能赋值但它们的行为有区别。set x to y让变量x指向y所指向的对象两者共用同一个数据。如果y是列表之后修改x里的元素y也会受影响。copy x to y把x的值复制一份给y两者互不影响。如果x是列表y得到一个副本各自独立变化。举个例子set listA to {1, 2, 3} set listB to listA set item 1 of listB to 99 -- 此时listA变成了 {99, 2, 3}因为set让listB和listA指向同一个列表。如果你一开始用的是copy listA to listB那listB修改就不会影响listA。新手写脚本时常因为“明明改了副本原数据却变了”而抓狂十有八九就是用了set。我的规则很简单只要不确定优先用copy避免意外修改原数据。3.4 运算符与表达式AppleScript的运算符大部分和主流语言相似但也有几个很容易搞混的写法。算术运算符加、减、乘、-、*除/结果通常是实数浮点数整数除法div例如7 div 2结果是 3取余mod例如7 mod 2结果是 1幂运算^例如2 ^ 3是 8注意AppleScript没有**这种写法幂运算就是^。比较运算符等于或is比如1 is 1不等于≠或用is not大于、小于、大于等于、小于等于、、≥、≤也可以用、包含contains比如hello contains ell开头/结尾判断starts with、ends with比较运算符的“英语风格”确实和其他语言很不一样is not、contains这类写法读起来就像在描述一句话但在编码时容易“不知道能不能这么写”。我的建议拿脚本编辑器试一下编译不报错就能用编译不过换个写法。用多了自然熟。逻辑运算符与and或or非not字符串拼接用符号比如Hello World结果就是Hello World。这里特别提醒两个字符串拼接没问题但字符串和数字直接拼会报错需要先把数字转成字符串用as string转换后面数据类型那节我详细讲。AppleScript里还有一种很常用的运算符叫限定词比如the、of。它们是语言的一部分用于指定对象关系典型写法tell application Finder set theName to name of the desktop end tell这里的the desktop不是变量“the”而是英文老式写法“这个桌面”。不过在现代AppleScript里the其实可加可不加建议初学者少用避免和普通变量混淆。4. 数据类型从字符串到文件一次分清4.1 字符串String字符串用双引号括起来注意必须用英文双引号。中文输入法下容易打出全角引号这是编译错误的重灾区。set greeting to 你好世界 set quotedText to 他说\你好\上面第二行用了\转义表示字符串里面包含双引号。如果没有转义他说你好会被解析成两个字符串直接编译报错。字符串常用操作set a to hello set b to world set c to a b -- hello world length of c -- 字符个数 get text 1 thru 5 of c -- 提取第1到第5个字符AppleScript字符串的一个特点是它按文本字符计数而不是字节所以中文、英文、emoji处理起来都相对省心。但提取子串用的text 1 thru 5这种英文句法纯新手会一脸懵其实就是“从第1个字符到第5个字符”的意思适应一下就好。4.2 整数与实数数字类型主要有两种integer整数和real实数即浮点数。set age to 30 -- integer set price to 19.99 -- real需要注意几点两个整数相除结果默认是real。7 / 2结果是3.5不是3。想取整除必须用div。整数太大超过系统上限或者发生小数运算时会自动转成实数AppleScript不会显式报错。数字和文本相互转换使用as42 as string、42 as integer。数字转成字符串在拼接时非常关键set n to 42 set s to 答案是 (n as string) -- 结果是 答案是 42直接答案是 n会编译报错这个坑我见了无数次。4.3 布尔值布尔值只有两个true和false注意是小写。AppleScript没有大写True/False的写法。set isReady to true if isReady then display dialog 准备好了 end if布尔运算中还会用到not、and、or。有趣的是AppleScript条件判断里的“真/假”写法非常口语化比如if x is not 0对应英文句子“if x is not 0”语义很直观。4.4 列表List列表相当于数组用花括号{}声明元素之间用逗号或and分隔set emptyList to {} set fruitList to {苹果, 香蕉, 橙子} set mixedList to {1, 文本, true, 3.14}列表索引从1开始这一点和很多语言不同。用item访问元素set firstFruit to item 1 of fruitList -- 苹果 set lastFruit to item -1 of fruitList -- 橙子-1表示从末尾数列表常用操作count of fruitList -- 3 length of fruitList -- 3两者可以互换 set combined to fruitList {葡萄} -- 末尾追加 set shorter to rest of fruitList -- 去掉第一个元素后的列表 reverse of fruitList -- 反转也可以用repeat with遍历列表repeat with f in fruitList display dialog f end repeatAppleScript对列表的修改要格外小心。直接set item 1 of fruitList to 猕猴桃可以修改但如果你一开始是用set把列表赋值给另一个变量修改时很容易波及原列表这里回到前面copy和set的话题再次强调。4.5 记录Record记录类似于Python字典或JSON对象用花括号和“键值对”表示set person to {name:张三, age:30, city:上海}键可以是标识符如name或者字符串取值用ofset theName to name of person -- 张三 set theAge to age of person -- 30 set persons city to 北京 -- 修改记录中的值persons city这种写法也是AppleScript特色相当于city of person。记录在存储元数据、解析简单配置时非常好用。但注意记录不是JSON它没有真正的“深拷贝”概念键的顺序在编译后可能被重新排列所以不要依赖键的顺序做逻辑判断。记录和列表可以嵌套组合出类似“对象数组”的结构set people to {¬ {name:李四, age:25}, ¬ {name:王五, age:35} ¬ }上面用了¬Option回车键打出表示行继续符AppleScript里一行写不下时可以用它换行阅读体验好很多。4.6 日期Date日期在AppleScript里是一等类型直接读系统时间set now to current date然后可以访问年、月、日、时、分、秒year of now month of now day of now hours of now minutes of now seconds of now weekday of now -- 星期几如 Saturday日期可以做算术运算单位是秒set tomorrow to now (1 * days) set oneHourLater to now (1 * hours)days、hours、minutes、seconds这些是系统预设的“秒数常量”1 * days就是一天的秒数。这种写法一开始会觉得绕但熟练掌握后做“距提醒时间还剩多少秒”之类的计算非常快。日期转成字符串时默认按系统区域格式输出比如“2024年12月25日 星期三 上午 10:30:00”。想做自定义格式反而不太容易通常要用text item delimiters或者直接调System Events我在后面一小节会提到简便方案。4.7 文件、别名与POSIX路径AppleScript处理文件时有三种常见的“文件表示法”新手很容易搞混alias文件/文件夹的别名对象特点是“保存引用”文件移动后依然能找到。缺点是如果原文件被删除引用会失效。file通用文件对象通过路径创建不做“引用追踪”。POSIX file用Unix风格路径如/Users/xxx/Desktop/a.txt创建的文件对象。典型写法set f1 to alias Macintosh HD:Users:xxx:Desktop:test.txt set f2 to POSIX file /Users/xxx/Desktop/test.txt注意AppleScript原生路径用“冒号”分隔不是斜杠。直接把/Users/...字符串传给alias会报错必须先转成POSIX file或者做路径转换。从POSIX路径获取别名用这条set theAlias to POSIX file /Users/xxx/Desktop/test.txt as alias这里我遇到过非常多的坑脚本里写死一个文件路径用alias引用结果文件被移动就崩了。所以如果脚本只是临时处理文件建议直接用POSIX file如果是做长期引用比如“定时备份固定目录”可以用alias让系统自动追踪。读取POSIX路径本身也常用set posixPath to POSIX path of (choose file) -- 得到 /Users/xxx/Desktop/a.txtchoose file是系统弹窗让用户选文件返回的也是文件对象配合POSIX path of就能拿到可读路径这是写交互式脚本时的高频操作。4.8 数据类型对照表如果你会其他语言下面是AppleScript常见类型与JavaScript/Python的粗略对应场景AppleScriptJavaScriptPython字符串hellohellohello整数424242实数3.143.143.14布尔true/falsetrue/falseTrue/False列表{1, 2, 3}[1, 2, 3][1, 2, 3]记录{name:张三}{name:张三}{name:张三}日期current datenew Date()datetime.now()这个表不是绝对等价但足够帮你把已有编程知识快速迁移过来。5. 控制流条件判断与三种循环5.1 if条件判断口语化写法条件判断的基本结构set score to 85 if score 90 then display dialog 优秀 else if score 60 then display dialog 及格 else display dialog 不及格 end ifthen关键字和结尾的end if都是固定写法漏了end if编译必然报错。值得一提的还有AppleScript的if可以写成单行if score 60 then display dialog 及格这种单行写法简洁但可读性差一点逻辑复杂时不要硬缩成一行会被自己坑到。条件判断里还有一个很常用的“列表成员判断”if 苹果 is in {苹果, 香蕉} then display dialog 有苹果is in是AppleScript独有的运算符判断一个元素是否在列表中用起来非常顺手。5.2 repeat循环固定次数与控制变量固定次数的循环典型写法repeat 3 times display dialog 循环 end repeat带变量的循环repeat with i from 1 to 5 display dialog 第 (i as string) 次 end repeat默认每次加1也可以指定步长repeat with i from 10 to 0 by -2 -- 10, 8, 6, 4, 2, 0 end repeatrepeat with i from ... to ... by ...是所有循环里最常用的遍历数字序列时比while更清晰。5.3 遍历列表的repeat遍历列表有两种方式。一种是前面写过的repeat with item in listset fruits to {苹果, 香蕉, 橙子} repeat with f in fruits display dialog f end repeat这种方式拿到的f是列表中的元素值直接可用。但要注意如果想在循环里顺便知道“当前是第几个”AppleScript没有内置的index变量通常要自己维护一个计数器set indexNum to 0 repeat with f in fruits set indexNum to indexNum 1 display dialog (indexNum as string) f end repeat另一种遍历方式是直接用索引repeat with i from 1 to (count of fruits) display dialog (item i of fruits) end repeat两种等价哪种顺手用哪种。我建议在需要修改列表元素时优先用索引方式因为repeat with f in fruits里的f是元素的副本直接修改f不会影响原列表。5.4 repeat while条件循环repeat while在条件为真时持续循环set n to 0 repeat while n 5 set n to n 1 end repeat注意如果条件永远为真会死循环。AppleScript没有“安全超时”机制一旦死循环脚本只能强制结束——在脚本编辑器里按Command.句号可以停止执行。这是Mac自动化里相当实用的小技巧我在写长循环时经常靠它救命。6. 实操批量整理桌面文件的完整脚本6.1 需求把“截图文件”按周归档理论讲再多不如一个实际脚本。我拿一个改过无数遍的“桌面整理器”做例子。需求很简单桌面上经常堆满截图文件文件名以“截屏”开头格式为截屏2024-12-01 10.30.00.png我想把它们按“周”归档到~/Desktop/整理/2024-W49这种文件夹里。这里的关键点有三个获取桌面路径、遍历文件、按规则创建新目录并移动。完整脚本如下-- 桌面整理把文件名包含截屏的文件按周归档 set desktopPath to POSIX path of (path to desktop folder) set targetRoot to desktopPath 整理/ tell application System Events set desktopFiles to every file of folder desktopPath repeat with f in desktopFiles set fName to name of f if fName contains 截屏 then -- 提取日期字符串例如从 截屏2024-12-01 中截取 2024-12-01 set rawDate to text 3 thru 12 of fName -- 把2024-12-01拆成年、月、日 set y to text 1 thru 4 of rawDate set m to text 6 thru 7 of rawDate set d to text 9 thru 10 of rawDate -- 生成周标记第几周这里简化处理用月份代替 set weekFolder to targetRoot y - m create folder weekFolder with properties {hidden:false} move f to folder weekFolder end if end repeat end tell这段脚本用到了本文几乎全部基础语法变量、字符串提取、条件判断、列表遍历、文件对象和tell块。实际运行时文件名格式不同text 3 thru 12这种“按固定位置截取”就不够通用。更健壮的方式是用AppleScript的text item delimiters配合split或者干脆用Shell中转。但对基础篇来说这个例子已经足够演示语法怎么落地。6.2 逐行拆解关键点path to desktop folder返回桌面目录对象再经POSIX path of得到/Users/xxx/Desktop/这种路径用于后面拼接。tell application System Events这里用System Events而是直接用Finder是因为System Events对“文件系统操作”更底层遍历文件时速度更快跨应用控制时也是同理。而Finder的语义更偏用户视角适合“移动/重命名/删除”这种带“图标感”的操作。every file of folder desktopPath列出目录下的所有文件对象。text 3 thru 12AppleScript字符串按字符位截取的经典写法从第3个字符截到第12个字符正好得到2024-12-01。因为文件名格式是截屏2024-12-01 10.30.00前两个字符是“截屏”所以从第3位起。create folder weekFolder在System Events里可以直接创建文件夹但如果目标目录已存在会报错。实际使用建议先判断存在性这里为了示例从简。这个脚本改一改就能变成“按扩展名归类”“按日期归档照片”“给下载目录瘦身”核心逻辑就是“遍历条件移动”。7. 上手容易踩的坑类型、引号与调试技巧7.1 类型转换与比较的“自动陷阱”AppleScript在类型转换上很“自动”但这不一定是好事。典型例子是字符串和数字的比较set a to 10 set b to 9 if a b then display dialog a 大 else display dialog b 大 end if期望肯定是10 9所以a大但AppleScript可能会把a和b都按字符串比较或都按数字比较取决于上下文结果不稳定。我建议所有字符串和数字比较前显式转换if (a as integer) b then还有一种更隐蔽的当字符串里混有数字时12abc as integer会报错而不是自动转成12。所以做转换前最好用class of或正则校验一下。我自己还踩过“列表元素类型混搭”的坑。一个列表里全是数字用item 1 item 2没问题但如果某个元素不小心是字符串加法直接报“无法求和”。这类问题排查费劲根源还是在“AppleScript的隐式转换太隐晦”。写严谨脚本时多点类型判断比少点强。7.2 中文引号、全角符号与行继续符写中文环境下的AppleScript最让人崩溃的是输入法自动把引号替换成中文引号。例如display dialog “你好”看着没问题编译就是报错。解决办法是在脚本编辑器里写代码时先把输入法切到英文模式或者打完中文内容后手动按一下Esc把智能引号关掉。在编辑器偏好设置里也可以关闭“智能引号”选项强烈建议最早做这一步。另一个容易忽略的是行继续符¬。AppleScript一行写太长时可以按OptionReturn插入行继续符下一行接着写。这个符号在中文输入法下也能打出来但如果手滑打成了^编译同样会出问题。7.3 调试三板斧display dialog、log和tryAppleScript没有断点调试器除非用第三方工具我的调试方式基本就三招。第一招中间结果弹窗。display dialog 当前变量是 myVar这个最直接缺点是弹窗必须手动点击脚本会暂停。适合脚本中关键节点检查不适合循环体内频繁使用。第二招log输出。在脚本编辑器底部能看到日志区log输出的内容会显示在那里不会阻塞脚本log 变量值是 myVar这比display dialog方便多了不过只有脚本编辑器里才能看到日志用osascript命令行跑时看不到。所以很多时候我会把日志重定向到文件。第三招try错误捕获。try set x to abc as integer on error errMsg display dialog 出错了 errMsg end trytry是AppleScript里非常实用的结构遇到潜在会报错的操作比如用户取消了文件选择、文件不存在、类型转换失败把它包起来就不至于整个脚本崩溃。我在写面向不懂技术的同事使用的脚本时几乎必用try把错误转成友好提示。真实项目的调试经验告诉我AppleScript的问题大多不是“逻辑难”而是“语法怪”。看到编译报错先检查引号、检查end if/end repeat有没有丢、检查then有没有写、检查类型是否匹配这五项能解决八成编译错误。7.4 常用辅助工具与后续学习方向如果决定认真学AppleScript我的工具清单是官方语言指南AppleScript Language Guide虽然旧但语法定义最权威碰到“这个运算符到底能不能用”时就翻它。脚本编辑器本身忽略它的简陋UI把它当编译器用就够了。第三方工具Script Debugger带完整调试和代码提示适合中大型项目。付费但如果你要维护几百行的脚本这笔钱很值。快捷键和菜单模板脚本编辑器自带“文件-模板”和“帮助-AppleScript帮助”里边有很多示例脚本可以直接抄。如果你想继续深入我建议下一篇去学AppleScript里最核心的tell架构——也就是“如何向不同的应用发指令”以及如何在应用之间传递数据。基本语法和数据类型是地基把这份语法吃透后你会发现AppleScript真正强大的地方才开始浮现它能打通应用与应用的隔阂把你每个重复性操作都变成一键完成的流程。别再觉得它“过时”试试把一个简单的重复任务交给它你会回来感谢它的。
返回列表