在Mac OS(OS X)上登录时启动shell脚本

17

我有这个shell脚本Test.sh

#! /bin/bash

FILE_TO_CHECK="/Users/test/start.txt"
EXIT=0

while [ $EXIT -eq 0 ]; do
    if [ -f "$FILE_TO_CHECK" ]
        then
        /usr/bin/java -jar myapp.jar
        EXIT=1
    else
        sleep 30
    fi
done

我需要在登录后自动启动这个脚本。
因此,我将它放在 /System/Library/StartupItems/ 文件夹中的 Test 内。

当我重新启动 Mac 后,在我登录后什么也没有发生。 有任何线索吗?

我还尝试了使用 Automator,但结果相同:Java 程序未运行。


1
顺便提一下,您应该避免使用大写变量名,因为这些名称已被系统保留。 - tripleee
一个解决方案是将其封装为应用程序。这可以使用Platypus轻松完成:https://apple.stackexchange.com/a/224507/86486 - AnneTheAgile
3个回答

49

Ivan Kovacevic的指针,特别是superuser.com链接非常有帮助。自至少OS X 10.9.2以来,您创建“运行时登录脚本”的选项如下:

注:这些方法被注释为是否:

  • 特定于给定用户(“ [ 用户特定] ”),即必须对每个用户执行安装,如果需要,则通常将脚本存储在用户特定位置,并且不需要根(管理员)权限。
  • 对所有用户有效(“ [ 所有用户] ”);即安装对所有用户生效,脚本通常存储在共享位置中,并且需要root(管理员)权限进行安装。

脚本本身将以不可见方式运行,但除了com.apple.loginwindow登录挂钩方法之外,您可以从它们中打开应用程序;请注意以下内容:

  • 没有保证任何这样的应用程序将处于最前面,因此可能会被其他在登录期间打开的窗口遮挡。
  • 如果您想要可视地运行另一个shell脚本,请使用open /path/to/your-script,它将在Terminal.app中打开它;但是,当您的脚本终止时,终端窗口将自动关闭。

自动操作程序 [用户特定]:

  • 文件 > 新建,输入应用程序
  • 添加一个运行Shell脚本操作,该操作添加了一个嵌入式bash脚本,并在其中粘贴您的脚本代码或添加调用现有脚本的命令。
  • 保存*.app包并将其添加到系统偏好设置 > 用户和组 > 登录项列表中。
  • 注:

    • 嵌入式脚本使用默认的"C"区域设置。
    • $PATH固定为/usr/bin:/bin:/usr/sbin:/sbin,不包括/usr/local/bin
    • 工作目录是当前用户的主目录

com.apple.loginwindow登录挂钩[所有用户-已弃用,但仍可使用]

如果您具有管理员特权,这是最简单的方法,但出于各种原因(安全,限于单个共享脚本,同步执行),已被弃用。尤其是苹果公司警告不要将此机制作为软件产品的一部分使用。

  • 将您的脚本(例如Test.sh)放在共享位置上,例如/Users/Shared,并确保其可执行(chmod +x /Users/Shared/Test.sh)。
  • Terminal.app运行以下内容:

    sudo defaults write com.apple.loginwindow LoginHook /Users/Shared/Test.sh

  • 注意:

    • 脚本将以root用户身份运行,因此请格外小心
      在此列出的方法中,这是唯一以root身份运行脚本的方法。

    • 只有一个系统级登录钩子。

      • 请注意,还有一个登出钩子LogoutHook,它提供了在注销时运行的功能——与其他方法不同。
    • 登录钩子脚本在其他登录操作之前同步运行,因此应该保持简短

      • 值得注意的是,它在桌面显示之前运行;您无法从脚本中启动应用程序,但是可以通过osascript和AppleScript片段创建简单的交互(例如,osascript -e 'display dialog "Proceed?"'),但是任何交互都会阻止登录过程。
    • 脚本在root用户的上下文中运行,并将登录的用户的用户名作为第一个参数传递给脚本。

    • 脚本使用默认的"C"语言环境。
    • $PATH固定为/usr/bin:/bin:/usr/sbin:/sbin,特别说明不包括/usr/local/bin
    • 工作目录为/

launchd代理:

launchd-agent执行的脚本可安装于特定用户或所有用户——后者需要管理员权限。

使用launchd是苹果公司推荐的方法,但这也是最繁琐的方法,因为它需要创建一个单独的*.plist配置文件。
好处是,您可以独立安装多个脚本。

  • 注意:
    • 不能保证launchd脚本的具体时间或顺序; 概括地说,它们“在登录时同时运行”; 甚至没有保证用户特定任务和所有用户任务之间的时序。
    • 脚本以默认的"C"语言环境运行。
    • $PATH固定为/usr/bin:/bin:/usr/sbin:/sbin,值得注意的是,其中不包括/usr/local/bin
    • 默认工作目录为/,但您可以通过.plist文件进行配置 - 见下文。
    • 脚本文件路径必须指定为完整的、文字的路径(例如:/Users/jdoe/script.sh;值得注意的是,以~为前缀的路径起作用。
    • 有关可用于*.plist配置文件中的所有键的描述,请参见man launchd.plist
    • 用户特定和所有用户任务都以当前用户(登录的用户)运行。

launchd [用户特定]:

  • 注意:Lingon 3(截至2014年初为5美元)是一个GUI应用程序,可简化下面的过程,但仅适用于用户特定脚本。
  • 将您的脚本(例如Test.sh)放在您的主文件夹中,例如/Users/jdoe
  • ~/Library/LaunchAgents中创建一个扩展名为.plist的文件,例如~/Library/LaunchAgents/LoginScripts.Test.plist,方法如下:

    touch ~/Library/LaunchAgents/LoginScripts.Test.plist
    
    打开文件并将其保存为以下内容:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
          <!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE -->
        <string>LoginScripts.Test.sh</string>
        <key>ProgramArguments</key>
        <array>
              <!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE -->
            <string>/Users/jdoe/Test.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
    </dict>
    </plist>
    
  • <!-- ... -->注释表示需要自定义的位置;您可以选择标签,但应保持唯一性 - 对于.plist文件名也是如此;为简单起见,请将标签和文件名根保持相同。

  • Terminal.app中运行以下命令:

  • launchctl load ~/Library/LaunchAgents/LoginScripts.Test.plist
    
    注意,作为副作用,脚本将立即执行。从那时起,只要当前用户登录,脚本就会执行。 不严格需要运行 launchctl load,因为由于文件的位置,它将在下次登录时自动被捡起, 但对于验证文件是否正确加载很有帮助。
    在一个共享位置(例如 /Users/Shared)放置你的脚本,例如 Test.sh。 通过在 Terminal.app 中运行以下命令,在 /Library/LaunchAgents 中创建一个带有扩展名 .plist 的文件(需要管理员权限),例如 /Library/LaunchAgents/LoginScripts.Test.plist:
    sudo touch /Library/LaunchAgents/LoginScripts.Test.plist
    
    打开文件并将其保存为以下内容(确保您的文本编辑器可以根据需要提示管理员权限;或者,使用 sudo nano /Library/LaunchAgents/LoginScripts.Test.plist):
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
          <!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE -->
        <string>LoginScripts.Test.sh</string>
        <key>ProgramArguments</key>
        <array>
              <!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE -->
            <string>/Users/Shared/Test.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
    </dict>
    </plist>
    
  • <!-- ... --> 注释表明了需要自定义的位置;您可以选择一个标签,但它应该是唯一的 - 同样适用于 .plist 文件名;为了简单起见,请保持标签和文件名根相同。

  • Terminal.app 运行以下命令:

  • sudo chown root /Library/LaunchAgents/LoginScripts.Test.plist
    sudo launchctl load /Library/LaunchAgents/LoginScripts.Test.plist
    
  • 注意,作为副作用,脚本将立即执行。自此以后,每当有任何用户登录时,脚本都会执行。

  • 不严格需要运行 launchctl load -- 因为由于文件的位置,它将在下次登录时自动被拾取 -- 但验证文件是否正确加载会很有帮助。

虽然这是一个很好的答案,但启动代理的PATH信息可能不正确。我不得不添加/usr/local/bin/:/bin/:/usr/bin/。这些在脚本可用的PATH中尚未包含 - @mklement0所称的“固定”路径。 - stonedauwg
就像我说的那样,我总共必须添加3个路径,其中2个是你说的固定路径,即/usr/bin和/bin。我的脚本抱怨在这些路径下找不到工具。一旦我添加了这些路径(还有其他工具的/usr/local/bin),它就可以看到这3个路径下的所有内容。我的观点是,在我的macOS 10.14环境中,/usr/bin和/bin并不是固定的 - 我必须显式地添加它们。/usr/local/bin我同意,它也不是固定的。 - stonedauwg
@stonedauwg:所以,明确一下:你是说当通过launchd运行时,你看到的唯一的“PATH”条目是“/usr/sbin:/sbin”吗?根据我的个人经验,我会说这非常不寻常,并且在网上搜索时,我看到了与我的答案中相同的目录列表的参考资料(例如,https://apple.stackexchange.com/a/284758/28668,https://serverfault.com/q/694439/176094)。因此,如果您能解释_为什么和何时_会看到不同的路径-例如在您的情况下-那将是有帮助的。 - mklement0
1
你是正确的。我试图将$PATH作为plist变量新路径的一部分使用,但我发现从ServerFault文章中得知这是行不通的。在launchd plists中,变量扩展是不起作用的,因此你需要自己指定所有的路径,基本上强制你重复默认值加上更多的内容 :( - stonedauwg
@filippo,我不会期望脚本因为机器进入睡眠状态而终止(但我从未测试过)。据我所知,没有“RunAtWake”事件类型或类似的事件类型可以在机器唤醒时运行某些东西。也许值得提出一个专注于您的问题的单独问题。 - mklement0
显示剩余3条评论

7
你不能仅将脚本放在那个文件夹中。你需要一个苹果所称的 "专业捆绑包",基本上就是一个包含可执行文件和.plist配置的文件夹。你应该将它放在/Library/StartupItems,因为/System/Library/StartupItems/ 是为操作系统保留的。在这里阅读有关此问题的所有信息:

https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpsystemstartup/chapters/StartupItems.html

还请注意,整个东西都被标记为已弃用技术。苹果建议使用launchd。这里有一个设置它的例子:

https://superuser.com/questions/229773/run-command-on-startup-login-mac-os-x


3

launchd-oneshot 用于将脚本安装为一个 launchd 工作,在登录时运行。

brew install cybertk/formulae/launchd-oneshot
sudo launchd-oneshot Test.sh --on-login

声明:本人是该软件包的作者。


5
感谢你想到一个在登录时作为root运行脚本的方法,并且通过CLI的名称暗示了这一点,但是为了明确起见,需要指出:它仅用于一次性运行脚本。我建议你在回答中说明这是你的一个项目。 - mklement0
实际上,Stack Overflow的推广政策要求您披露您与所推荐资源的任何关联。我会编辑您的回答以符合要求。 - tripleee

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