【问题标题】:`os.symlink` vs `ln -s``os.symlink` 与 `ln -s`
【发布时间】:2013-03-12 21:42:55
【问题描述】:

我需要为 dir2 中的每一项 dir1(文件或目录)创建一个符号链接。 dir2 已经存在并且不是符号链接。在 Bash 中,我可以通过以下方式轻松实现:

ln -s /home/guest/dir1/* /home/guest/dir2/

但是在 python 中使用os.symlink 我得到一个错误:

>>> os.symlink('/home/guest/dir1/*', '/home/guest/dir2/')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 17] File exist

我知道我可以使用subprocess 并运行ln 命令。我不想要那个解决方案。

我也知道使用os.walkglob.glob 的解决方法是可能的,但我想知道是否可以使用os.symlink 来做到这一点。

【问题讨论】:

  • 认为 os.symlink 仅仅是相应系统调用的包装器(嗯,或多或少),因此不会提供与完整的系统调用相同的语义-使用该系统调用的成熟实用程序。
  • “我想使用os.symlink”要求的原因是什么?如果正确的做法是在 glob 上循环 for,那么您为什么不想做正确的事情呢?
  • @abarnert 如果必须,我会这样做。我想知道os.symlink 是否有办法做到这一点。我以为它会提供 ln -s 所做的一切,但事实证明它没有。
  • @jurgenreza:你仍然错过了关键点:你正在寻找的不是 ln -s 所做的,而是 shell 所做的。跨度>
  • @0xC0000022L:我并没有纠正你,而是消除了你的“我认为”前缀留下的任何疑问。

标签: python linux bash symlink


【解决方案1】:

os.symlink 创建一个符号链接。

ln -s 创建多个符号链接(如果它的最后一个参数是一个目录,并且有多个源)。 Python 等价物类似于:

dst = args[-1]
for src in args[:-1]:
    os.symlink(src, os.path.join(dst, os.path.dirname(src)))

那么,ln -s /home/guest/dir1/* /home/guest/dir2/ 是如何工作的呢?您的 shell 通过将通配符转换为多个参数来实现这一点。如果您只使用带有通配符的exec ln 命令,它会在/home/guest/dir1/ 中查找一个字面上名为* 的源,而不是该目录中的所有文件。

Python 等价物类似于(如果您不介意将两个级别混合在一起并忽略许多其他情况 - 代字号、环境变量、命令替换等在 shell 中是可能的):

dst = args[-1]
for srcglob in args[:-1]:
    for src in glob.glob(srcglob):
        os.symlink(src, os.path.join(dst, os.path.dirname(src)))

你不能单独使用 os.symlink 来做到这一点——它的任何一部分——因为它不这样做。这就像说“我想在不过滤名称的情况下使用os.walk 做与find . -name foo 等效的操作。”或者,就此而言,我想做与 ln -s /home/guest/dir1/* /home/guest/dir2/ 等效的操作,而不需要 shell 为我提供通配符。”

正确的答案是使用glob,或fnmatch,或os.listdir加上一个正则表达式,或任何你喜欢的。

不要使用os.walk,因为它会递归文件系统遍历,所以它甚至不接近shell *扩展。

【讨论】:

  • ln is ln [target] [link],怎么会有你描述的几个target?我认为您的意思是链接到一个目标的多个链接?
【解决方案2】:

* 是一种外壳扩展模式,在您的情况下,它指定“所有以/home/guest/dir1/ 开头的文件”。

但是shell 的职责是将此模式扩展 到它匹配的文件。不是ln 命令的。

os.symlink 不是外壳,它是操作系统调用 - 因此,它不支持外壳扩展模式。您必须在脚本中完成这项工作。

为此,您可以使用os.walkos.listdir。如另一个答案所示,适当的调用将取决于您想要做什么。 (os.walk 不等同于 *


说服自己:在终端中的 Unix 机器上运行此命令:python -c "import sys; print sys.argv" *。您会看到进行匹配的是外壳。

【讨论】:

  • +1 用于关于 globbing 的实验。但是请记住,Windows shell (cmd.exe) 在将其传递给被调用的程序之前不会执行此操作。
  • @0xC0000022L:好吧,公平地说,Windows 不附带ln。并且 MSVCRT(他们的 libc)带有一个 hack,可以在您的 main 启动之前或当您明确要求它时模拟 argv 上的 shell globbing,我很确定大多数 Unix 程序端口要么使用它,要么等效的东西(例如,无论 Cygwin 做什么)。
  • @0xC0000022L 我必须承认检查这是否适用于 Windows 并没有引起我的注意 - 将更新!
  • @abarnert:嗯,他们称之为mklink,就像他们在*nix 世界中将已知的东西称为mkdir 在Windows 世界中简称为md
  • @0xC0000022L:但mklink 的语法与ln 不同。特别是,mklink 无法获取源列表并将它们全部链接到目录中;它需要一个源和一个目标。
【解决方案3】:

正如@abarnert 所建议的那样,它是识别* 并将其替换为dir1 内的所有项目的shell。因此我认为使用os.listdir 是最好的选择:

for item in os.listdir('/home/guest/dir1'):
    os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)

【讨论】:

  • 我建议使用os.path.join 而不是+
猜你喜欢
  • 1970-01-01
  • 2010-11-02
  • 2015-04-20
  • 2014-11-27
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多