将Shell脚本转换为*.app文件

15

使用 Automator.app 和 Platypus.app,我已经成功将一个用于在 MacBook Pro 上执行 Wi-Fi 电源循环的简单 shell 脚本打包成了两个应用程序。这两个应用程序都可以正常运行,但是有一个明显的问题需要纠正:这些应用程序引用了程序外部的 shell 脚本。如何将 shell 脚本嵌入应用程序资源中,并引用它,以便即使原始源文件被移动,应用程序也能正常运行?


1
交叉引用;https://www.sveinbjorn.org/platypus 我喜欢Platypus! - AnneTheAgile
2个回答

32

只是提一下,如果您在脚本上获取信息,您可以将其设置为使用终端打开。这将在双击脚本时运行它。

否则,将脚本打包成 .app 包非常简单。Mac OS X 将愉快地运行标识为应用程序可执行文件的任何脚本。

至少,您需要放置以下结构:

  • (名称).app
    • Contents
      • MacOS
        • (名称)

其中名为(名称)的文件是您的脚本(必须可执行,并且必须有 shebang 行)。(名称) 必须在 .app 目录和脚本文件中相同:例如,如果您的应用程序目录名为“我的 Shell 脚本.app”,那么 MacOS 目录中的文件必须被称为“我的 Shell 脚本”,没有扩展名

如果这不方便,可以使用 Info.plist 文件来指定替代可执行文件名。Info.plist 放在 Contents 目录中:

  • Wrapper.app
    • Contents
      • Info.plist
      • MacOS
        • MyScript

如果您在属性列表中将 MyScript 指定为 CFBundleExecutable,则此结构(Wrapper.app 中的 MyScript 可执行文件)有效:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key>
    <string>MyScript</string>
</dict>
</plist>

使用 Info.plist 文件可能更好,因为这样可以在不破坏它的情况下重命名包装器。

以下是一个示例脚本,它使用 /bin/sh 作为解释器,但你实际上可以使用任何东西 (#!/usr/bin/swift, #!/usr/bin/python 等等)。

#!/bin/sh
open -a Calculator

双击应用捆绑包即可运行脚本。

您可以在 Contents 目录中打包其他所需内容。如果感觉有余力,可以通过添加 Resources 目录等内容来重现标准可执行捆绑包布局。


这是我尝试的第一个解决方案。http://goo.gl/GgqaR6 显示了目录结构,应用程序图标被划掉的方式以及当我尝试运行它时出现的错误消息。除非我做错了什么。 - C1pher
你的cyclewifi文件是什么格式?以391K的大小来说,它对于一个shell脚本来说有点重了。(另外,我刚刚重新按照我在这里解释的方式做了一遍,它可以工作,所以我不确定问题出在哪里。) - zneak
1
那才391K,这就是你所有的内容吗?你考虑过从头开始写一个只有这个和shebang行的新脚本吗? - zneak
5
尽管这个方法可行,但我想指出,在MacOS中不能使用脚本对捆绑包进行代码签名。在MacOS目录中,非Mach-O文件的代码签名不会被维护。请参考苹果的代码签名指南以获取更多详情:https://developer.apple.com/library/content/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW7 - Jason
1
@Daniel,如果我没记错的话,macOS使用Apple Events来告诉你的程序打开一个文档。你无法从shell脚本中接收到这些事件。但是,从能够访问本地库的脚本语言中可能是可行的。 - zneak
显示剩余11条评论

0

虽然这样可以工作,但似乎没有办法从脚本中访问Contents目录。它没有通过环境变量传递给bash,也没有其他地方可以找到。这使得将东西捆绑到Contents目录中变得困难,因为似乎无法通过bash脚本访问它。网络上有各种各样的黑客技巧,但它们都涉及有效地按名称搜索脚本,这意味着如果应用程序的两个版本在两个位置,则会失败。 有人解决了这个问题吗?


3
在Bash脚本中,“$0”表示什么意思?如果它包含完整的路径,你可以使用“dirname”命令来进入Contents文件夹。 - jamieguinan
1
我已经使用$(cd "$(dirname "$0")"; pwd)让它正常工作了。 - Andreas Riedmüller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接