Копирование символической ссылки в Python - PullRequest
23 голосов
/ 31 января 2011

Я хочу скопировать файл src в место назначения dst, но если src окажется символической ссылкой, сохраните ссылку вместо копирования содержимого файла. После того, как копирование выполнено, os.readlink должно вернуть то же самое для src и dst.

Модуль shutil имеет несколько функций, таких как copyfile, copy и copy2, но все они копируют содержимое файла и не сохраняют ссылку , shutil.move имеет правильное поведение, за исключением того, что он удаляет исходный файл.

Есть ли в Python встроенный способ выполнить копирование файла при сохранении символических ссылок?

Ответы [ 2 ]

42 голосов
/ 31 января 2011

Просто сделайте

def copy(src, dst):
    if os.path.islink(src):
        linkto = os.readlink(src)
        os.symlink(linkto, dst)
    else:
        shutil.copy(src,dst)

shutil.copytree делает что-то похожее, но, как отметил отправитель, требовательно копировать только каталоги, а не отдельные файлы.

2 голосов

Python 3 follow_symlinks

В Python 3 большинство методов копирования shutil изучили аргумент follow_symlinks, который сохраняет символические ссылки, если выбран.

например. для shutil.copy:

shutil.copy(src, dest, follow_symlinks=False)

и в документах написано :

shutil.copy(src, dst, *, follow_symlinks=True)

Копирует файл src в файл или каталог dst. src и dst должны быть строками. Если dst указывает каталог, файл будет скопирован в dst с использованием базового имени файла из src. Возвращает путь к вновь созданному файлу.

Если follow_symlinks равно false, а src является символической ссылкой, dst будет создан как символическая ссылка. Если follow_symlinks` имеет значение true и src является символической ссылкой, dst будет копией файла, на который ссылается src.

Однако есть одна проблема: если вы попытаетесь перезаписать существующий файл или символическую ссылку, произойдет сбой с:

FileExistsError: [Errno 17] File exists: 'b' -> 'c'

в отличие от follow_symlinks=True, который успешно перезаписывается.

То же самое происходит и для os.symlink, поэтому я вместо этого использовал:

#!/usr/bin/env python3

import shutil
import os

def copy(src, dst):
    if os.path.islink(src):
        if os.path.lexists(dst):
            os.unlink(dst)
        linkto = os.readlink(src)
        os.symlink(linkto, dst)
    else:
        shutil.copy(src, dst)

if __name__ == '__main__':
    os.symlink('c', 'b')
    os.symlink('b', 'a')
    copy('a', 'b')

    with open('c', 'w') as f:
        f.write('a')
    with open('d', 'w'):
        pass
    copy('c', 'd')
    copy('a', 'c')

Протестировано в Ubuntu 18.10, Python 3.6.7.

...