`os.symlink` vs` ln -s`
Мне нужно создать символическую ссылку для каждого элемента dir1 (файл или каталог) внутри dir2. 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.walk
или glob.glob
, но я хочу знать, возможно ли это сделать с помощью os.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/
? Ваша оболочка делает это, превращая шаблон в несколько аргументов. Если бы вы просто указали exec
команду ln
с подстановочным знаком, она бы искала один источник с буквальным названием *
в /home/guest/dir1/
, а не все файлы в этом каталоге.
Эквивалент Python выглядит примерно так (если вы не возражаете смешивать два уровня вместе и игнорировать множество других случаев - тильды, переменные env, подстановку команд и т.д., Которые возможны в оболочке):
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
, или fnmatch
, или os.listdir
, и регулярное выражение, или все, что вы предпочитаете.
Не используйте os.walk
, потому что это делает рекурсивное обход файловой системы, так что это даже близко не к расширению оболочки *
.
Ответ 2
*
- это шаблон расширения оболочки, который в вашем случае обозначает "все файлы, начинающиеся с /home/guest/dir1/
".
Но это ваша роль оболочки, чтобы расширить этот шаблон до файлов, которые он соответствует. Не команда ln
.
Но os.symlink
не является оболочкой, это вызов ОС - следовательно, он не поддерживает шаблоны расширения оболочки. Вам нужно будет выполнить эту работу в script.
Для этого вы можете использовать os.walk
или os.listdir
. Как указано в другом ответе, соответствующий вызов будет зависеть от того, что вы хотите сделать. (os.walk
не будет эквивалентом *
)
Чтобы убедить себя: запустите эту команду на машине Unix в вашем терминале: python -c "import sys; print sys.argv" *
. Вы увидите, что это оболочка, которая выполняет сопоставление.
Ответ 3
Как было предложено @abarnert, это оболочка, которая распознает *
и заменяет ее всеми элементами insir dir1. Поэтому я думаю, что использование os.listdir
- лучший выбор:
for item in os.listdir('/home/guest/dir1'):
os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)