ChatGPT解决这个技术问题 Extra ChatGPT

python:将脚本工作目录更改为脚本自己的目录

我每分钟从 crontab 运行一个 python shell:

* * * * * /home/udi/foo/bar.py

/home/udi/foo 有一些必要的子目录,例如 /home/udi/foo/bar.py 所指的 /home/udi/foo/log/home/udi/foo/config

问题是 crontab 从不同的工作目录运行脚本,因此尝试打开 ./log/bar.log 失败。

有没有一种好方法可以告诉脚本将工作目录更改为脚本自己的目录?我想要一个适用于任何脚本位置的解决方案,而不是明确告诉脚本它在哪里。

编辑:

os.chdir(os.path.dirname(sys.argv[0]))

是最紧凑优雅的解决方案。感谢您的回答和解释!

crontab 用例无关:如果使用 execfile() 运行脚本,sys.argv[0]__file__ 都会失败; inspect-based solution 可以改为使用。

E
Eli Courtwright

这会将您当前的工作目录更改为以便打开相对路径将起作用:

import os
os.chdir("/home/udi/foo")

但是,您询问了如何更改 Python 脚本所在的目录,即使您在编写脚本时不知道该目录是什么目录。为此,您可以使用 os.path 函数:

import os

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

这将获取脚本的文件名,将其转换为绝对路径,然后提取该路径的目录,然后更改为该目录。


等于硬编码目录。
如果您从符号链接运行它,这将不起作用。使用 __file__ 而不是 sys.argv[0]
为什么是 abspath 步骤?为什么不简单地os.chdir(os.path.dirname(__file__))
__file__ 在“冻结”程序(使用 py2exe、PyInstaller、cx_Freeze 创建)中失败。 sys.argv[0] 有效。 @ChrisDown:如果您想关注符号链接; os.path.realpath() 可以使用。
@EliCourtwright 如果 __file__ 还不是绝对路径,并且用户更改了工作目录,那么 os.path.abspath 无论如何都会失败。
x
xverges

您可以使用 sys.path[0] 获得更短的版本。

os.chdir(sys.path[0])

来自http://docs.python.org/library/sys.html#sys.path

在程序启动时初始化,此列表的第一项路径 [0] 是包含用于调用 Python 解释器的脚本的目录


请注意,如果您 import git,这将不起作用。第一个路径将指向 <pythondir>/lib/site-packages/git/ext/gitdb。至少这发生在我身上
谢谢,@marco。我刚刚为此 github.com/gitpython-developers/GitPython/pull/1068 创建了 PR
S
S.Lott

不要这样做。

你的脚本和你的数据不应该混合到一个大目录中。将您的代码放在与您的数据分开的某个已知位置(site-packages/var/opt/udi 或其他位置)。对代码使用良好的版本控制,以确保当前和以前的版本相互分离,以便您可以回退到以前的版本并测试未来的版本。

底线:不要混合代码和数据。

数据很宝贵。代码来来去去。

提供工作目录作为命令行参数值。您可以提供默认值作为环境变量。不要推断(或猜测)

使其成为必需的参数值并执行此操作。

import sys
import os
working= os.environ.get("WORKING_DIRECTORY","/some/default")
if len(sys.argv) > 1: working = sys.argv[1]
os.chdir( working )

不要根据软件的位置“假设”一个目录。从长远来看,它不会很好地工作。


我认为您对大型软件包的代码和数据分离是正确的,但对于小型维护脚本来说似乎有些牵强。我完全同意版本控制。
S. Lott 是对的。始终保持数据和代码分开,除非数据不是暂时的。例如,如果您有图标,那就是数据,但它不是暂时的,并且相对于软件包(无论这意味着什么)考虑它是有意义的
@Udi Pasmon:一点也不牵强。正是“小型维护脚本”让组织陷入了深深的困境。多年后,这个“小型维护脚本”及其子项、派生项和数据文件将成为解开和重新实现的噩梦。尽可能让数据远离代码——为所有东西传递参数——什么都不做。
+1 我以为我想担任 OP,但在阅读了您的建议后,我改为修改了我的脚本。现在它需要一个参数来指定日志文件的位置。
+1。如果可以轻松自定义数据目录,则更容易为 python 脚本创建包 (rpm)。
R
Ruud Althuizen

将您的 crontab 命令更改为

* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py)

(...) 启动您的 crond 作为单个命令执行的子 shell。如果目录不可用,|| exit 1 会导致您的 cronjob 失败。

尽管从长远来看,对于您的特定脚本,其他解决方案可能更优雅,但在您无法修改要执行的程序或命令的情况下,我的示例仍然很有用。


这是一个非常合理的解决方案。我通常会发现自己在编辑其他人的答案以添加 || exit 1 之类的内容。看到这个让人耳目一新。虽然我确实想知道为什么你不只是做cd /home/udi/foo/ && ./bar.py
@BrunoBronosky 使用明确的 exit 1 您的 crond 将收到错误通知,并且在大多数情况下会发送失败的电子邮件通知。
@RuudAlthuizen cd /home/udi/foo/ && ./bar.py 也会这样做。