I need to create a symlink for every item of dir1 (file or directory) inside dir2. dir2 already exists and is not a symlink. In Bash I can easily achieve this by:
ln -s /home/guest/dir1/* /home/guest/dir2/
But in python using os.symlink I get an error:
>>> os.symlink('/home/guest/dir1/*', '/home/guest/dir2/') Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 17] File exist
I know I can use
subprocess and run
ln command. I don’t want that solution.
I’m also aware that workarounds using
glob.glob are possible, but I want to know if it is possible to do this using
os.symlink creates a single symlink.
ln -s creates multiple symlinks (if its last argument is a directory, and there’s more than one source). The Python equivalent is something like:
dst = args[-1] for src in args[:-1]: os.symlink(src, os.path.join(dst, os.path.dirname(src)))
So, how does it work when you do
ln -s /home/guest/dir1/* /home/guest/dir2/? Your shell makes that work, by turning the wildcard into multiple arguments. If you were to just
ln command with a wildcard, it would look for a single source literally named
/home/guest/dir1/, not all files in that directory.
The Python equivalent is something like (if you don’t mind mixing two levels together and ignoring a lot of other cases—tildes, env variables, command substitution, etc. that are possible at the 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)))
You can’t do that with
os.symlink alone—either part of it—because it doesn’t do that. It’s like saying “I want to do the equivalent of
find . -name foo using
os.walk without filtering on the name.” Or, for that matter, I want to do the equivalent of
ln -s /home/guest/dir1/* /home/guest/dir2/ without the shell globbing for me.”
The right answer is to use
os.listdir plus a regex, or whatever you prefer.
Do not use
os.walk, because that does a recursive filesystem walk, so it’s not even close to shell
* is a shell extension pattern, which in your case designates “all files starting with
But it’s your shell’s role to expand this pattern to the files it matches. Not the
os.symlink is not a shell, it’s an OS call – hence, it doesn’t support shell extension patterns. You’ll have to do that work in your script.
To do so, you can use
os.listdir. As indicated in the other answer, the appropriate call will depend on what you want to do. (
os.walk wouldn’t be the equivalent of
To convince yourself: run this command on an Unix machine in your terminal:
python -c "import sys; print sys.argv" *. You’ll see that it’s the shell that’s doing the matching.
As suggested by @abarnert it’s the shell that recognizes
* and replaces it with all the items insside dir1. Therefore I think using
os.listdir is the best choice:
for item in os.listdir('/home/guest/dir1'): os.symlink('/home/guest/dir1/' + item, '/home/guest/dir2/' + item)