51

我需要为里面的 dir1 (文件或目录)的每个项目创建一个符号链接dir2dir2已经存在并且不是符号链接。在 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.walkor的解决方法glob.glob是可能的,但我想知道是否可以使用os.symlink.

4

3 回答 3

68

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的命令,它会查找字面上命名为inln的单个源,而不是该目录中的所有文件。*/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一点——它的任何一部分——因为它不会那样做。这就像说“我想做相当于find . -name foo使用os.walk而不过滤名称。” 或者,就此而言,我想做相当于ln -s /home/guest/dir1/* /home/guest/dir2/没有外壳为我服务的事情。”

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

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

于 2013-03-22T21:52:58.540 回答
15

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

但是,将这种模式扩展到它匹配的文件是你的shell 的角色。不是ln命令的。

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

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


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

于 2013-03-22T21:52:51.497 回答
6

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

for item in os.listdir('/home/guest/dir1'):
    os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)
于 2013-05-03T20:14:01.197 回答