【问题标题】:running a shell script automatically with launchd on Mac在 Mac 上使用 launchd 自动运行 shell 脚本
【发布时间】:2020-11-20 00:06:21
【问题描述】:

我正在尝试让脚本每天同时运行以重新启动 Mac。我无法使用节能首选项面板中的内置计划功能,因为我运行的应用程序阻止了正常重启。如果我通过终端中的shutdown 命令启动重新启动,无论哪些应用程序可能阻止它,它都会强制重新启动。

我对 shell 脚本和启动非常陌生,所以我真的是新手,所以请记住这一点。这是我在这里的第一篇文章,但多年来我定期访问并通常找到我正在寻找的答案。

尝试了各种方法后,我有点遇到了障碍。我所在的位置是我的 /Library/LaunchDaemons 文件夹中有一个 .plist 文件,该文件指向应该在特定时间运行的脚本。我最初尝试使用在线教程中的代码在 bbedit 中编写 .plist 文件。它总是无法加载到launchctl(手动或通过重新启动)。然后我尝试在 Xcode 中使用 plist 编辑器,似乎效果更好。

完成后,plist 将加载到 launchctl。它似乎也尝试在指定时间运行脚本。我查看了控制台,似乎 Mac 确实尝试运行脚本,但失败并出现以下错误:

com.apple.xpc.launchd[1] (com.apple.restart.sh[940]):服务退出,异常代码:1

我尝试直接从终端运行脚本,例如sh scriptname,然后 Mac 重新启动。

这是脚本:

#!/bin/bash
shutdown -r now

我将脚本保存在 /Library/Scripts 文件夹中。

这是.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>
    <string>com.apple.restart.sh</string>
    <key>Program</key>
    <string>/Library/Scripts/com.apple.restart.sh</string>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>50</integer>
    </dict>
</dict>
</plist>

我现在真的不知道出了什么问题,因为 .plist 似乎已成功加载到 launchctl 并且脚本尝试在指定时间运行但失败了。如果脚本是手动运行的,它就可以工作。

我最好的猜测是这与权限或所有权有关,但这只是我的直觉。我试过在文件上运行chown root:wheel。这将错误更改为:

异常代码78

如有任何帮助,我们将不胜感激。

编辑来自https://stackoverflow.com/users/2836621/mark-setchell的以下请求

最新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>
    <string>com.erithacus.restart</string>
    <key>Program</key>
    <string>/Library/Scripts/com.erithacus.restart.sh</string>
    <key>StandardOutPath</key>
    <string>/tmp/com.erithacus.restart.stdout</string>
    <key>StandardErrorPath</key>
    <string>/tmp/com.erithacus.restart.stderr</string>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>11</integer>
        <key>Minute</key>
        <integer>10</integer>
    </dict>
</dict>
</plist>

最新脚本内容:

#!/bin/bash

/sbin/shutdown -r now >>/tmp/shutdown.log 2&>1

.stdout 文件的内容为空。

.stderr的内容如下:/Library/Scripts/com.erithacus.restart.sh: line 3: 1: Read-only file system

这可能与 macOS Catalina 的操作系统位于只读分区上有关吗?

编辑 2. 一个解决方案。

最终的 plist 文件 (com.erithacus.restart.sh) 内容(不过必须使用 Xcode 制作):

<?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>
    <string>com.erithacus.restart</string>
    <key>Program</key>
    <string>/Library/Scripts/com.erithacus.restart.sh</string>
    <key>StandardOutPath</key>
    <string>/tmp/com.erithacus.restart.stdout</string>
    <key>StandardErrorPath</key>
    <string>/tmp/com.erithacus.restart.stderr</string>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>12</integer>
        <key>Minute</key>
        <integer>10</integer>
    </dict>
</dict>
</plist>

(显然改变StartCalendarInterval下的时间以适应)

这个 plist 保存在 /Library/LaunchDaemons

最终脚本(com.erithacus.restart.sh):

#!/bin/bash

date +"%F %T Shutting down"

/sbin/shutdown -r now

脚本保存在 /Library/Scripts 中

我认为,与原始文件不同的关键是我使用sudo chown root [plist file name] 将plist 文件的所有权更改为root

然后我必须以 root 身份将其加载到launchctl,即sudo launchctl load [plist file name]。奇怪的是,当以这种方式加载时,它不会显示命令launchctl list,但会显示sudo launchctl list

这导致系统成功定时重启计算机,从而覆盖任何可能阻止重启的应用程序。这对于我想在每晚凌晨 5 点重新启动的服务器很有帮助(因此显然 plist 文件中的时间现在将更改为在那个时间运行)。

特别感谢@marksetchell,他的错误记录建议帮助我解决了这个问题。

【问题讨论】:

  • 脚本在哪个账户下运行?可以添加调试吗? (如shutdown -r now &gt;&gt;/tmp/shutdown.log 并确保运行脚本的用户对该文件具有写入权限。)
  • 另外,您自己的脚本可能看起来不应该属于com.apple
  • 谢谢,我会试一试的。我尝试了各种名称,并且在某处我读到有人说要尝试。同意,这很奇怪。将尝试再次将其更改为其他内容。我对 launchd 的理解是它们由系统以 root 身份运行,所以我不确定用户是否相关?至少,我正在尝试达成一种用户不重要的安排,并且无论如何它都会运行。
  • 我会想象它以 root 身份运行,但不应该出现错误,所以这就是我在这一点上的推测。我认为 launchd 可以指定以哪个用户身份运行。
  • 哦,当然还有重定向标准错误,否则日志可能是空的。 shutdown -r now &gt;&gt;/tmp/shutdown.log 2&amp;&gt;1 ... 很抱歉没有提到这一点。

标签: bash macos sh restart launchd


【解决方案1】:

需要检查的几件事。


首先,确保你的脚本是可执行的:

chmod +x /Library/Scripts/com.apple.restart.sh

其次,将shutdown 的完整路径放入脚本中以确保可以找到它:

#!/bin/bash
/sbin/shutdown -r now

我通过运行发现:

which shutdown

输出

/sbin/shutdown

第三,确保您的脚本是纯 ASCII 文本,而不是 RTF 或 PagesMS-Word 文档:

file /Library/Scripts/com.apple.restart.sh

输出

/Library/Scripts/com.apple.restart.sh: Bourne-Again shell script text executable, ASCII text

第四,在launchctl 之外测试您的脚本,以确保它首先独立运行:

/Library/Scripts/com.apple.restart.sh

第五,在您的plist 文件中像这样重定向输出:

<key>StandardOutPath</key>
<string>/tmp/com.apple.restart.stdout</string>
<key>StandardErrorPath</key>
<string>/tmp/com.apple.restart.stderr</string>

【讨论】:

  • 感谢您如此详尽的回复。我已经能够从终端成功运行脚本文件。这会排除您的一些建议吗?无论如何,我检查了ASCII并且输出是正确的。
  • 这些步骤是有序的,所以如果您进入第 4 步,则无需担心第 1..3 步中的任何事情。我想它仍然无法正常工作,所以您能否在您的问题下单击edit 并在您重新启动计算机并加载后添加您最新、最大的plist 文件,以及/tmp/com.apple.restart.stderr 和相应的stdout 的内容请问launchctl工作?
  • 您也可以在关闭脚本中添加一行,就在您运行 /sbin/shutdown 之前执行 date +"%F %T Shutting down"
  • 编辑:您的最后两个 cmets 在我发布此内容后出现。会照你说的做。我已经关注了您注意到的所有内容,这是 .stderr 日志中的错误:/Library/Scripts/com.erithacus.restart.sh:第 3 行:1:只读文件系统
  • 一个问题,date +"%F %T Shutting down" 是做什么的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多