将变量从 bash 传递到 applescript - 无法输入文本。 (-1700) 错误
Passing variables from bash to applescript - can't make into type text. (-1700) Error
我正在尝试在自动化器中制作脚本来自动化一些 shell 脚本。我希望文件夹位置是动态的。我已经设法将变量从 bash 传递给 applescript,但是 applescript 给出了类型错误 (-1700) "Can't make type into text"。我在这里错过了什么?
x="/"
my_command="cd $x"
osascript -e 'on run my_command' -e \
'tell application "Terminal"
do shell script my_command
activate
end tell' -e 'end run' $my_command
这里的命令 ('cd /') 不会 运行 并且它会给我以下类型错误。
47:67: execution error: Terminal got an error: Can’t make {"cd", "/"} into type text. (-1700)
我假设您的代码块正在 Automator 运行 Shell Script 操作中使用(这是您的问题中要包含的重要内容,以供将来参考)。根据这个假设来解决您的特定查询,关于 bash 和 AppleScript 中的变量,有两个小花絮需要注意:
在bash中,shell会处理参数扩展(比如你使用$my_command
变量作为脚本最后一行 osascript
的参数)和 命令替换 (其中变量的使用方式与参数的使用方式类似,只是它包含一个shell 命令),并执行称为 分词 的操作。顾名思义,它将文本拆分为单词,尽管在 shell 脚本的上下文中,word 由 $IFS
环境中定义的任何字符分隔多变的。如果您特别感兴趣,您可以自己阅读更多内容,但是这在您的脚本中所做的是您从称为 word splitting 的东西中预测的:它拆分 "cd /"
在 whitespace,并为每个单词提供两个单独的字段,从而将 两个 个参数传递给 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"
和"/"
,传递给 osascript
; osascript
将每个单独的参数解释为 list
中的 item
以发送到 run
处理程序。值得庆幸的是,您使用了第一个 AppleScript run
处理程序语法将您的参数声明为不定长度的 list
对象。
这允许向 AppleScript 变量 my_command
传递一个包含两项的列表,即 {"cd", "/"}
。如果您知道要如何处理列表中的项目,这是可以接受的:在这种情况下,您可能希望使用 space 分隔符连接它们;直接的方法是这样的:
item 1 of my_command & space & item 2 of my_command --> "cd /"
由于没有这样做,do shell script
(无论如何这是错误的命令)收到一个 list
对象作为其 直接参数 ,而它需要一个 text
(string
) 对象。
我可能会选择解决此特定问题的方法是防止 bash 中的单词拆分。这样做的方法是用双引号将参数括起来:
osascript -e 'on run my_command' ... -e 'end run' "$my_command"
双引号告诉 shell 将里面的所有内容都视为一个单词,所以现在 AppleScript 变量 my_command
接收一个参数。从技术上讲,这仍然是一个列表,包含一个项目,但是大多数期望 text
对象的命令在接收到包含字符串的单项列表时能够表现得很好。
然后,正如@user3439894 在评论中指出的那样,您应该将 do shell script
替换为 do script
,它将在新的 终端中执行命令 "cd /"
tab/window.
实现此修复的调整脚本可能如下所示:
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"
综上所述,在这种情况下,使用 运行 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 表达式的字符串。
这可以通过它之前的任何操作提供给 运行 AppleScript 操作,我建议它是纯文本; Automator 变量;或者特定于参数性质的东西(在这种情况下,一个目录,建议 Finder 操作是合适的)。由于您希望保持目录动态,Get Selected Finder Items 将是一个如何向 Automator 提供 Finder 的示例 路径,并将其作为传递给 AppleScript 的参数。
因此,我将做一些小的改动,首先是将 cd
命令引入 AppleScript,这样您就不必再与其他 Automator 混为一谈了行动并找出如何正确组合它们的方法。我还要修改 run
处理程序参数表达式:
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 中选择的文件、文件夹或项目的文件路径,并打开 终端 window 将其工作目录指向包含所选文件的文件夹。
这绝不是完美的实现(而且,从表面上看,macOS 有一个内置服务可以打开指向当前的 Terminal window在 Finder 中打开文件夹)。但是,对于与 shell 通信的 5 行 AppleScript 代码块,它是可行的。
我的参数声明的这个漂亮而狂野的表达式:
on run {{filepath}, null}
在 filepath
参数周围添加了花括号,从而将其限制为一个一元文件路径参数。它无需担心进行多项选择时会发生什么,因为只会存储选择中的第一项。这意味着不选择任何项目并触发脚本会抛出错误,但如果您删除大括号则相反(多选会抛出错误)。
有很多方法可以愉快地管理所有场景,但是这个答案超出了最初的预期范围。
我正在尝试在自动化器中制作脚本来自动化一些 shell 脚本。我希望文件夹位置是动态的。我已经设法将变量从 bash 传递给 applescript,但是 applescript 给出了类型错误 (-1700) "Can't make type into text"。我在这里错过了什么?
x="/"
my_command="cd $x"
osascript -e 'on run my_command' -e \
'tell application "Terminal"
do shell script my_command
activate
end tell' -e 'end run' $my_command
这里的命令 ('cd /') 不会 运行 并且它会给我以下类型错误。
47:67: execution error: Terminal got an error: Can’t make {"cd", "/"} into type text. (-1700)
我假设您的代码块正在 Automator 运行 Shell Script 操作中使用(这是您的问题中要包含的重要内容,以供将来参考)。根据这个假设来解决您的特定查询,关于 bash 和 AppleScript 中的变量,有两个小花絮需要注意:
在bash中,shell会处理参数扩展(比如你使用
$my_command
变量作为脚本最后一行osascript
的参数)和 命令替换 (其中变量的使用方式与参数的使用方式类似,只是它包含一个shell 命令),并执行称为 分词 的操作。顾名思义,它将文本拆分为单词,尽管在 shell 脚本的上下文中,word 由$IFS
环境中定义的任何字符分隔多变的。如果您特别感兴趣,您可以自己阅读更多内容,但是这在您的脚本中所做的是您从称为 word splitting 的东西中预测的:它拆分"cd /"
在 whitespace,并为每个单词提供两个单独的字段,从而将 两个 个参数传递给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"
和"/"
,传递给 osascript
; osascript
将每个单独的参数解释为 list
中的 item
以发送到 run
处理程序。值得庆幸的是,您使用了第一个 AppleScript run
处理程序语法将您的参数声明为不定长度的 list
对象。
这允许向 AppleScript 变量 my_command
传递一个包含两项的列表,即 {"cd", "/"}
。如果您知道要如何处理列表中的项目,这是可以接受的:在这种情况下,您可能希望使用 space 分隔符连接它们;直接的方法是这样的:
item 1 of my_command & space & item 2 of my_command --> "cd /"
由于没有这样做,do shell script
(无论如何这是错误的命令)收到一个 list
对象作为其 直接参数 ,而它需要一个 text
(string
) 对象。
我可能会选择解决此特定问题的方法是防止 bash 中的单词拆分。这样做的方法是用双引号将参数括起来:
osascript -e 'on run my_command' ... -e 'end run' "$my_command"
双引号告诉 shell 将里面的所有内容都视为一个单词,所以现在 AppleScript 变量 my_command
接收一个参数。从技术上讲,这仍然是一个列表,包含一个项目,但是大多数期望 text
对象的命令在接收到包含字符串的单项列表时能够表现得很好。
然后,正如@user3439894 在评论中指出的那样,您应该将 do shell script
替换为 do script
,它将在新的 终端中执行命令 "cd /"
tab/window.
实现此修复的调整脚本可能如下所示:
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"
综上所述,在这种情况下,使用 运行 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 表达式的字符串。
这可以通过它之前的任何操作提供给 运行 AppleScript 操作,我建议它是纯文本; Automator 变量;或者特定于参数性质的东西(在这种情况下,一个目录,建议 Finder 操作是合适的)。由于您希望保持目录动态,Get Selected Finder Items 将是一个如何向 Automator 提供 Finder 的示例 路径,并将其作为传递给 AppleScript 的参数。
因此,我将做一些小的改动,首先是将 cd
命令引入 AppleScript,这样您就不必再与其他 Automator 混为一谈了行动并找出如何正确组合它们的方法。我还要修改 run
处理程序参数表达式:
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 中选择的文件、文件夹或项目的文件路径,并打开 终端 window 将其工作目录指向包含所选文件的文件夹。
这绝不是完美的实现(而且,从表面上看,macOS 有一个内置服务可以打开指向当前的 Terminal window在 Finder 中打开文件夹)。但是,对于与 shell 通信的 5 行 AppleScript 代码块,它是可行的。
我的参数声明的这个漂亮而狂野的表达式:
on run {{filepath}, null}
在 filepath
参数周围添加了花括号,从而将其限制为一个一元文件路径参数。它无需担心进行多项选择时会发生什么,因为只会存储选择中的第一项。这意味着不选择任何项目并触发脚本会抛出错误,但如果您删除大括号则相反(多选会抛出错误)。
有很多方法可以愉快地管理所有场景,但是这个答案超出了最初的预期范围。