首页 > 解决方案 > 从 AppleScript 启动 Python 脚本会在读取一行时产生 EOFError

问题描述

我希望使用 AppleScript 来启动一个 Python 脚本,它的前几行是:

x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDate = input("Enter date of Bookmap replay data as YYYY-DD-MM: ")

if userDate != "":
    dateStamp=userDate

从终端(在 Mac 上)运行 Python 脚本时,我没有任何问题:系统提示我输入日期,如果我没有提供日期,脚本将使用当前日期。

但是,当从 AppleScript 启动 Python 脚本时,我无法提供输入,因为会立即抛出 EOF 错误。

我的整个 AppleScript 由

do shell script "source ~/.bash_profile; python3 /PythonScript.py"

并且仅用作启动 Python 脚本的一种方式。

作为一种解决方法,我创建了一个ExecutePythonScript.sh带有文本的 shell 脚本 ( ),python3 /PythonScript.py然后调整了我的 AppleScript 以启动 shell 脚本:

do shell script "/ExecutePythonScript.sh"

不幸的是,AppleScript 和 Python 之间的这一额外距离并没有帮助。

有没有办法允许通过终端输入,即使从 AppleScript 启动?

如果没有,有没有办法通过 AppleScript 请求输入并将该输入传递给 Python 脚本?

出于好奇,我在启动 Python 之前使用 AppleScript 执行其他功能(通过 Hammerspoon)。因此,AppleScript 充当了各种需要发生的事情的中央命令。

谢谢你的帮助。

标签: python-3.xapplescripteofeoferror

解决方案


我对 Hammerspoon 不熟悉,但是这里有几个示例可供考虑。

简单示例

考虑以下简单.py示例,该示例提示通过 AppleScript 进行输入并将该输入传递回 Python 脚本。

蟒蛇脚本.py

import datetime
from subprocess import Popen, PIPE

x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDatePrompt = """
  on getDateFromUser()
    tell application "System Events"
      activate
      set mssg to "Enter date of Bookmap replay data as YYYY-DD-MM:"
      set usrPrompt to display dialog mssg default answer "" buttons {"Cancel", "Continue"} default button "Continue"
      return text returned of usrPrompt
    end tell
  end getDateFromUser

  return getDateFromUser()
"""

proc = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
userDate, error = proc.communicate(userDatePrompt)

userDate = userDate.split("\n")[0]

if userDate != "":
    dateStamp = userDate

解释

  • 这利用 PythonPopen.communicate()本质上生成了一个调用 AppleScript 对话框的新进程 - 提示用户输入日期。

  • 用于提示对话框的 AppleScript 被分配给该userDatePrompt变量。

  • 在用户输入一些值(假定为日期)后,它会被传回 Python 脚本并分配给userData变量。

  • 请注意以下内容:

    userDate = userDate.split("\n")[0]
    

    我们首先split将字符串分配给userDate变量,使用换行符\n作为分隔符,然后访问数组中的第一项(即在索引处0),然后检查该值是否不是空字符串("")。我们这样做是为了确保如果用户要么;单击Cancel按钮,或单击Continue按钮而不输入值,我们使用您的默认日期值(即今天/现在)

注意:上述脚本的主要问题是我们不以任何方式验证用户输入。例如,他们可以;输入foo bar quux,然后单击Continue按钮,我们的脚本将愉快地假定分配给userDate变量的值是有效日期。最终导致我们的程序在我们开始使用该值时在某处中断。


验证示例

以下更全面的示例.py另外验证用户输入的值是否是符合的有效日期YYYY-DD-MM

蟒蛇脚本.py

import datetime
from subprocess import Popen, PIPE


x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDatePrompt = """
  on dateIsValid(givenDate)
    set statusCode to do shell script "date -j -f \\"%Y-%d-%m\\" " & quoted form of givenDate & " > /dev/null 2>&1; echo $?"
    if statusCode is equal to "0" then
      return true
    else
      return false
    end if
  end dateIsValid

  on getDateFromUser()
    tell application "System Events"
      activate
      set mssg to "Enter date of Bookmap replay data as YYYY-DD-MM:"
      set usrPrompt to display dialog mssg default answer "" buttons {"Use Default Date", "Continue"} default button "Continue" cancel button "Use Default Date"
      return text returned of usrPrompt
    end tell
  end getDateFromUser

  repeat
    set dateReceived to my getDateFromUser()
    set isValidDate to dateIsValid(dateReceived)
    try
      if isValidDate then
        exit repeat
      else
        error
      end if
    on error
      tell application "System Events"
        activate
        display dialog quote & dateReceived & quote & " is not a valid date." & return & return & "Please try again." buttons {"OK"} default button 1 with icon caution
      end tell
    end try
  end repeat

  return dateReceived
"""

proc = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
userDate, error = proc.communicate(userDatePrompt)

userDate = userDate.split("\n")[0]

if userDate != "":
    dateStamp = userDate

解释

  • 这基本上与前面的(简单)示例相同,但是它提供了所有通过 AppleScript 处理的附加验证。
  • 根据前面的示例,我们提示用户通过getDateFromUser函数输入日期。这次Cancel按钮已更改为 aUse Default Date以更好地指示它的实际作用。
  • 随后,我们通过该dateIsValid函数验证用户输入。此功能利用 shellsdate实用程序/命令来检查日期是否符合YYYY-DD-MM.
  • 通过该repeat语句,我们基本上反复提示用户,直到提供有效日期 - 仅在遇到有效日期时退出。

跑步

  • 通过 AppleScript:

    与您的示例类似,要python-script.py通过 AppleScript 运行,请使用以下do shell script命令

    do shell script "source ~/.bash_profile; python3 ~/Desktop/python-script.py"
    
  • 通过终端:

    或通过终端运行:

    source ~/.bash_profile; python3 ~/Desktop/python-script.py
    

注意:路径名;~/Desktop/python-script.py,在这两个示例中都需要根据需要重新定义。还给出了我的回答中提供的示例,这source ~/.bash_profile;部分并不是绝对必要的。


推荐阅读