【问题标题】:passing information from one script to another将信息从一个脚本传递到另一个脚本
【发布时间】:2014-07-01 00:02:21
【问题描述】:

我有两个 python 脚本,scriptA 和 scriptB,它们在 Unix 系统上运行。 scriptA 需要 20 秒运行并生成一个数字 X。 scriptB 运行时需要 X,大约需要 500 毫秒。我需要每天运行 scriptB,但每个月只需要运行一次 scriptA。所以我不想从 scriptB 运行 scriptA。我也不想每次运行 scriptA 时都手动编辑 scriptB。我想过通过 scriptA 更新文件,但我不确定这样的文件可以理想地放置在哪里,以便 scriptB 以后可以读取它;独立于这两个脚本的位置。将这个值 X 存储在 Unix 系统中以供 scriptB 以后使用的最佳方式是什么?

【问题讨论】:

  • 你考虑过环境变量吗?

标签: python linux unix


【解决方案1】:

Linux/Unix 中的许多程序将配置保存在 /etc/ 中,并将 /var/ 中的子文件夹用于其他文件。
但您可能需要 root 权限。

如果您在主文件夹中运行脚本,则可以创建文件 ~/.scripB.rc 或文件夹 ~/.scriptB/~/.config/scriptB/

参见*Filesystem Hierarchy Standard

【讨论】:

  • 嘿弗拉斯。感谢您的回答。 scriptB 将来可能被其他用户使用。我确信所有运行 scriptB 的人都将拥有 root 权限。所以我将在 /var/lib 或 /var/cache 中创建一个子文件夹。在 scriptA 中,我将在此子文件夹中创建一个文件并对其进行更新。在 scriptB 中,我将对该文件位置进行硬编码并从中进行访问。如果您发现这种方法有任何问题,请告诉我
  • 对我来说似乎还可以。
【解决方案2】:

听起来您想序列化 ScriptA 的结果,将其保存在某个文件或数据库中,然后让 ScriptB 读取这些结果(可能还修改文件或更新数据库条目以表明这些结果现在已被处理)。

要完成这项工作,您需要让 ScriptA 和 ScriptB 就数据的位置和格式达成一致……并且您可能希望实现某种锁定以确保 ScriptB 不会以损坏的输入告终恰好在 ScriptA 写入或更新数据的同时运行(相反,ScriptA 不会在 ScriptB 访问数据时写入数据而破坏数据存储)。

当然,ScriptA 和 ScriptB 都可以将文件名或其他数据位置硬编码到它们的源中。但是,这将违反DRY Principle。因此,您可能希望他们共享一个配置文件。 (当然,配置文件名也在这些来源中重复......或者至少是配置代码的公共位的import......但后者仍然确保安装/配置详细信息(位置和可能的格式,数据存储的)与源代码解耦。因此可以更改(在共享配置中)而不影响任一脚本的其余代码。

至于具体使用哪种类型的文件和序列化......这是一个不同的问题。

现在,尽管听起来很奇怪,但我建议使用SQLite3。使用 SQL“数据库”来简单地存储单个值似乎有点过头了。但是 SQLite3 包含在 Python 标准库中,只需要一个文件名即可配置。

您也可以使用pickleJSON 甚至YAML(这将需要第三方模块)...甚至只是使用struct 之类的文本或一些二进制表示。但是,其中任何一个都需要您解析结果并处理任何解析或格式错误。 JSON 将是这些替代方案中最简单的选择。此外,如果您希望 ScriptA 和 ScriptB(可能还有您为操作此特定数据而编写的任何其他脚本)对任何并发操作的机会都具有鲁棒性,则您必须自己进行文件锁定和处理。

SQLite3 的优势在于它为您处理解析和解码以及锁定和并发。您创建一次表(可能嵌入在 ScriptA 中作为一个很少使用的“--initdb”选项,用于需要重新创建数据存储的场合)。你的阅读代码可能看起来很简单:

#!/usr/bin/python
import sqlite3
db = sqlite3.connect('./foo.db')
cur = db.cursor()
results = cur.execute(SELECT value, MAX(date) FROM results').fetchone()[0]

...写一个新值看起来有点像:

#!/usr/bin/python
# (Same import, db= and cur= from above)
with db:
    cur.execute('INSERT INTO results (value)  VALUES (?)', (myvalue,))

所有这一切都假设您曾在某个时候使用以下内容初始化数据存储(在此示例中为 foo.db):

#!/usr/bin/python
# (Same import, db= and cur= from above)
with db:
    cur.execute('CREATE TABLE IF NOT EXISTS results (value INTEGER NOT NULL, date TIMESTAMP DEFAULT current_timestamp)')

(实际上,如果您希望脚本从清除旧数据中静默恢复,您可以每次都执行该命令)。

这似乎比基于 JSON 文件的方法更多的代码。然而,SQLite3 提供了ACID(transactional) 语义以及抽象出序列化和反序列化。

另外请注意,我在掩饰一些细节。我上面的示例实际上是创建一个完整的结果表,其中包含将它们写入数据存储区的时间戳。这些会随着时间的推移而累积,如果您使用这种方法,您会定期使用以下命令清理“结果”表:

#!/usr/bin/python
# (Same import, db= and cur= from above)
with db:
    cur.execute('DELETE FROM results where date < ?', cur.execute('SELECT MAX(date) from results').fetchone())

或者,如果您真的不想访问从 INSERT 变为 UPDATE 的先前结果,如下所示:

#!/usr/bin/python
# (Same import, db= and cur= from above)
with db:
    cur.execute(cur.execute('UPDATE results SET value=(?)', (mynewvalue,))

(还要注意(mynewvalue,) 是一个单元素元组。DBAPI 要求将我们的参数包装在元组中,当您第一次使用这样的单参数开始使用它时很容易忘记)。

显然,如果您采用这种仅更新的方法,您可以从“结果”表中删除“日期”列,并从查询中删除所有对 MAX(data) 的引用。

在我的早期示例中,我选择使用稍微复杂一点的架构,因为它们可以让您的脚本更加健壮,而且几乎没有额外的复杂性。然后,您可以进行其他错误检查,例如检测 ScriptB 发现 ScriptA 未按预期运行的缺失值。

【讨论】:

  • 您好,感谢您写得很好的答案,列出了所有可能性。我想我会用@furas 回答这个案例,但我肯定会尝试更多地探索 SQLite3。
【解决方案3】:

编辑/运行crontab -e:

# this will run every month on the 25th at 2am
0 2 25 * * python /path/to/scriptA.py > /dev/null

# this will run every day at 2:10 am
10 2 * * * python /path/to/scriptB.py > /dev/null

为两个脚本创建一个外部文件:

在脚本A中:

>>> with open('/path/to/test_doc','w+') as f:
...     f.write('1')
... 

在脚本B中:

>>> with open('/path/to/test_doc','r') as f:
...     v = f.read()
...
>>> v
'1'

【讨论】:

  • 嘿jmunsch。感谢您的回答。也许我不清楚我的问题。我知道在 python 中打开、关闭和读取文件的方式。我实际上想知道在 Unix 系统中应该将这样的文件放在哪里
  • 啊对不起,是的,那么 furas 有你要找的东西 /etc /var /tmp 或在隐藏文件夹的主目录中 ~/.somefolder 都是这些类型变量所在的地方为各种程序存储。
【解决方案4】:

你可以看看PyPubSub 它是一个 python 包,提供了一个发布 - 订阅 Python API,便于基于事件的编程。

它将为您的问题提供独立于操作系统的解决方案,并且只需要在 A 和 B 中添加几行代码。

你也不需要处理杂乱的文件!

【讨论】:

  • 您好,感谢您的回答。 PyPubSub 可用于两个并行线程之间的信息交换,对吗?我认为这对我的情况没有帮助。有错请指正
  • @Sanket 不幸的是,是的。我想到的另一个想法是在 A 中编写一个服务器程序,将数据发送到指定端口的本地主机,并让脚本 B 在 while True: 循环中侦听该端口!
【解决方案5】:

假设您没有同时运行这两个脚本,您可以(pickle 和)将对象之间的跳转保存在任何地方,只要您在加载和保存文件时指向相同的系统路径。例如:

import pickle  # or import cPickle as pickle

# Create a python object like a dictionary, list, etc.
favorite_color = { "lion": "yellow", "kitty": "red" }

# Write to file ScriptA
f_myfile = open('C:\\My Documents\\My Favorite Folder\\myfile.pickle', 'wb')
pickle.dump(favorite_color, f_myfile)
f_myfile.close()

# Read from file ScriptB
f_myfile = open('C:\\My Documents\\My Favorite Folder\\myfile.pickle', 'rb')
favorite_color = pickle.load(f_myfile)  # variables come out in the order you put them in
f_myfile.close()

【讨论】:

  • 嘿迈克尔。感谢您的回答。我知道在 python 中打开、关闭和读取文件的方式。我实际上想知道在 Unix 系统中应该将这样的文件放在哪里。
  • 也使用linux,没有最好的地方。你会得到关于程序经常把输出放在哪里的答案,但重要的一点是它在你有权写入的地方并且不会被删除。