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

Ada*_*tan 153 python working-directory

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

* * * * * /home/udi/foo/bar.py
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑:

os.chdir(os.path.dirname(sys.argv[0]))
Run Code Online (Sandbox Code Playgroud)

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

Eli*_*ght 185

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

import os
os.chdir("/home/udi/foo")
Run Code Online (Sandbox Code Playgroud)

但是,您询问如何更改为Python脚本所在的目录,即使您不知道编写脚本时将使用哪个目录.为此,您可以使用以下os.path功能:

import os

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
Run Code Online (Sandbox Code Playgroud)

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

  • `__file__`在"冻结"程序中失败(使用py2exe,PyInstaller,cx_Freeze创建).`sys.argv [0]`有效.@ChrisDown:如果你想遵循符号链接; 可以使用`os.path.realpath()`. (7认同)
  • 等于对目录进行硬编码。 (2认同)
  • 如果从符号链接运行它,这将不起作用.使用`__file__`而不是`sys.argv [0]`. (2认同)
  • 为什么要进行绝对步骤?为什么不简单地使用 `os.chdir(os.path.dirname(__file__))` 呢? (2认同)
  • @EliCourtwright如果`__file__`不是绝对路径,并且用户已经更改了工作目录,那么`os.path.abspath`无论如何都会失败. (2认同)

xve*_*ges 45

你可以使用更短的版本sys.path[0].

os.chdir(sys.path[0])
Run Code Online (Sandbox Code Playgroud)

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

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


S.L*_*ott 23

不要这样做.

您的脚本和数据不应该被嵌入到一个大目录中.将代码放在与数据分开的某个已知位置(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 )
Run Code Online (Sandbox Code Playgroud)

不要"假设"基于软件位置的目录.从长远来看,它不会很好.

  • 我认为你为大型软件包分离代码和数据是正确的,但对于一个小的维护脚本来说似乎很牵强.我完全同意版本控制. (8认同)
  • @Udi Pasmon:根本不是牵强附会.这是"小维护脚本",让组织陷入深深的麻烦.多年以后,这个"小维护脚本"及其子项,派生词和数据文件将成为解开和重新实现的噩梦.保持数据尽可能远离代码 - 为所有内容传递参数 - 不假设任何内容. (5认同)
  • S. Lott是对的.始终保持数据和代码分离,除非数据不是暂时的.例如,如果你有图标,那就是数据,但它不是暂时的,考虑它相对于软件包是有意义的(无论这意味着什么) (2认同)

Ruu*_*zen 17

将您的crontab命令更改为

* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py)
Run Code Online (Sandbox Code Playgroud)

(...)启动子shell,你的crond的执行作为一个单一的命令.将|| exit 1导致你的cronjob,该目录是不可用的情况下失败.

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

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