我假设您的代码块正在 Automator Run Shell Script 操作中使用(这是包含在您的问题中的重要内容,以供将来参考)。根据这个假设来解决您的特定查询,关于 bash 和 AppleScript 中的变量需要注意两个小花絮:
在 bash 中,shell 将处理 参数扩展(例如您在脚本的最后一行使用 $my_command 变量作为 osascript 的参数)和 命令替换(其中变量的使用方式与参数类似,除了它包含一个shell命令),并执行称为分词的操作。顾名思义,它将文本拆分为单词,尽管在 shell 脚本的上下文中,word 由 $IFS 环境变量中定义的任何字符分隔。如果您特别感兴趣,您可以自己阅读更多内容,但是这在您的脚本中所做的是您从称为 分词 的东西中预测的内容:它在空格处拆分 "cd /",并且为每个单词提供两个单独的字段,因此将 两个 参数传递给 osascript,而不是您想要的一个参数。
-
在 AppleScript 中,传递给 run 处理程序的参数可以用两种通用语法形式定义。第一个是:
on run args
其中args 是一个变量,将使用传递给它的非特定数量的参数(可能为零)填充并生成一个list 对象,其中列表中的每个item 是其中之一参数(保留顺序)。
第二种语法形式将变量标识符括在大括号内,以声明固定数量的参数列表:
on run {arg}
这里,{arg} 是一个单项list 对象,定义了精确数量的参数(在本例中为一个),这些参数将传递给变量arg。传递少于一个参数将在 AppleScript 中引发错误;传递 more 会将第一个参数存储在 arg 中,丢弃其余参数。需要声明两个参数:
on run {_1, _2}
第一个参数传递给变量_1,第二个参数传递给变量_2(所有这些例子中变量名的选择除了象征意义之外没有任何意义,但它们也是例子有效的 AppleScript 变量标识符)。传递的参数少于两个会抛出错误;传递两个以上的参数将破坏除前两个参数之外的所有参数。
您可以遵循与增加在处理程序中声明的参数数量相同的规则,一般形式为:
on run {a, b, c, ...}
另外请注意,这些变量可以本身包含对象的list。因此,虽然on run {arg} 将接收单个参数,但存储在变量arg,该参数可能是一个包含多个项目的列表。
考虑到两种不同语言的这两个特性,也许你可以看到发生了什么:$my_command 进行了参数扩展,将其拆分为两个参数,"cd" 和 "/",然后传递给 @987654348 @; osascript 将每个单独的参数解释为 list 中的 item 以发送到 run 处理程序。幸运的是,您使用了第一个 AppleScript run 处理程序语法将您的参数声明为无限长度的 list 对象。
这允许将 AppleScript 变量 my_command 传递给包含两项的列表,即 {"cd", "/"}。如果您知道如何处理列表中的项目,这是可以接受的:在这种情况下,您可能希望使用空格分隔符将它们连接起来;直接的方式是这样的:
item 1 of my_command & space & item 2 of my_command --> "cd /"
由于这没有完成,do shell script(无论如何,这是错误的命令)接收到一个 list 对象作为它的 直接参数,而它期望一个 text (@987654361 @) 对象。
我可能会选择解决此特定问题的方法是防止 bash 中的单词拆分。这样做的方法是将参数括在双引号中:
osascript -e 'on run my_command' ... -e 'end run' "$my_command"
双引号告诉 shell 把里面的所有东西都当作一个单词,所以现在 AppleScript 变量 my_command 接收一个参数。从技术上讲,这仍然是一个包含一个项目的列表,但大多数期望 text 对象的命令在收到包含字符串的单项目列表时都能够表现得更合理。
然后,正如@user3439894 在评论中指出的那样,您应该将do shell script 替换为do script,它将在新的终端选项卡/窗口中执行命令"cd /"。 p>
实现此修复的调整后脚本可能如下所示:
x="/"
my_command="cd $x"
osascript -e '
on run my_command
tell application "Terminal"
do script my_command
activate
end tell
end run' "$my_command"
话虽如此,在这种情况下,使用 Run AppleScript 操作会更明智,如下所示:
on run {my_command, null}
tell application "Terminal"
do script my_command
activate
end tell
end run
在这里,我声明run 处理程序正好传递两个参数:
第二个是 Automator 特有的,它通常有一个示例代码,可以将第二个参数存储在一个名为 parameters 的变量中。此参数由 Automator 传递,因此您无需担心它的来源。它包含与该脚本实例的运行相关的本地目录信息。在我遇到的任何情况下,它都不是特别有用,当然也不是在这种情况下。我更喜欢通过将第二个参数声明为null 来销毁信息。
这给我们留下了只有 一个 参数,但这个参数将以list 的形式到达,我建议在开始时——在你决定你想要更多复杂性之前——限制到单个项目,这将是一个包含您的 bash 表达式的字符串。
这可以通过它之前的任何操作提供给 Run AppleScript 操作,我建议使用纯文本; Automator 变量;或特定于参数性质的东西(在这种情况下,一个目录,建议 Finder 操作是合适的)。由于您希望保持目录动态,Get Selected Finder Items 将是如何向 Automator 提供 Finder 路径的一个示例,并具有成为传递给 AppleScript 的参数。
因此,我将做一些小改动,首先将 cd 命令引入 AppleScript 中,这样您就不必纠结于额外的 Automator 操作并弄清楚如何组合他们正确。我还要修改run handler参数表达式:
on run {{filepath}, null}
local filepath
set filepath to the POSIX path of ([filepath, "::"] as text)
tell application "Terminal"
do script "cd " & the quoted form of the filepath
activate
end tell
end run
此工作流程现在接收您在 Finder 中选择的文件、文件夹或项目的文件路径,并打开一个 终端 窗口以将其工作目录指向包含所选文件的文件夹。
这绝不是说明这一点的完美实现(而且,在表面上,macOS 有一个内置服务来打开一个 Terminal 窗口,该窗口指向 Finder 中当前打开的文件夹em>)。但是,对于与 shell 通信的 5 行 AppleScript 代码块,它是可行的。
我的参数声明的这个漂亮而狂野的表达:
on run {{filepath}, null}
在filepath 参数周围添加了花括号,从而将其限制为一个精确的一元文件路径参数。它无需担心进行多项选择时会发生什么,因为只有选择中的第一项被存储。这意味着不选择任何项目并触发脚本会引发错误,但如果您删除大括号(多选会引发错误),则相反。
有很多方法可以愉快地管理所有场景,但这个答案会超出最初预期的范围。