Copying, Moving, and Renaming Files
00:00 In this lesson, I’m going to tell you a little bit about how to copy, move, and rename files and directories.
00:08
There are several useful functions for copying and moving files. For copying, there are two options—or, actually, three options with the shutil
(shell utilities) module, which you’ll have to import.
00:19
There’s copy()
, which just takes in a source and a destination file path, and copies the file from the source path to the destination path. So the source file will still exist, of course.
00:31
You’re just copying, you’re not deleting the first file. The important thing about copy()
, though, is that it copies the file, the contents of the file, but not its metadata.
00:40
So, for example, things like its last modification time, and so on, will not be preserved. That will all just be reset to the baseline values. If you do need the metadata, you can use the shutil.copy2()
function, which has identical syntax, it just copies the metadata, as well.
00:57
So, those are distinct use cases. Then you have shutil.copytree()
, which copies full directory trees. This is what you should use if you want to copy directories in any capacity, but just keep in mind that it will copy the whole tree rooted at src_dir
, and it will copy it into the destination directory.
01:18
For moving—or renaming, because, really, the two are essentially equivalent—you have two options: one with the shutil
module and one with os
.
01:27
shutil.move()
, it takes in, again, a source and a destination parameter and it moves the file or directory from src
(source) to dst
(destination).
01:36
Then os.rename()
really does essentially the same thing because moving and renaming are, really, two sides of the same coin, so to speak. It renames the file or directory with the old and the new name.
01:48 So, those are all pretty good and convenient. Let’s take a look at the sample directory, real quick, before moving into the Python REPL. The sample directory has a couple of folders, I’ve used this one before: two folders, a few Python files in each, and then a couple of text files at the top-level.
02:07
All right, I’ve imported os
and shutil
, shell util, already. The first thing to do is to show off the functionality of shutil.copy()
.
02:18
I’ll just copy test1.txt
into a new file called backup.txt
02:26
and I’ll call it backup1.txt
just to make sure, you know, that it’s a backup of test1.txt
. As you can see, shutil.copy()
returns the path of the newly copied file backup1.txt
.
02:40
And if I say os.listdir()
, you can see that there’s now a test1.txt
and backup1.txt
. So this original file is preserved, and the new backup is also created.
02:51
And those files will have exactly the same content, but they will not have the same os.stat()
.
03:01 So, as you can see there, the modification times are different, not by a huge factor, but that’s because I just created these files recently. And then the atime, as well, is pretty different, And the creation time is different, too, different ID numbers.
03:17
So a lot of these are quite different, even though they’ve been copied. If you really want to copy this metadata and preserve the modification times and stuff like that, then you’ll have to use shutil.copy2()
.
03:30
I’ll use that on test2.txt
, for
03:33
consistency, I’ll make a new file called backup2.txt
. So that’s been copied, you can see that it’s worked. There’s a backup2.txt
here.
03:43 I really should have put some content in these text files—right now they’re just blank files—so that I could show you that the contents are actually exactly the same.
03:51
Just trust me when I say that they are. And then you can take a look at the stat()
here and see test2.txt
. os.stat('backup2.txt')
,
04:04 And as you can see, this atime and the modification time are now exactly the same. What has remained different though, are the ctime, the absolute creation time, which makes sense because you don’t want to copy over the creation time, because that just doesn’t make sense, right?
04:21 This file was still created by the copy, even though it’s supposed to have the same metadata as this. Then the ID number, which is a unique system identifier really should be different as well, because otherwise, your OS won’t know what to refer to these files by, because it just uses bare numbers to get these files.
04:38
So, that’s how you can copy this metadata stuff, but just be aware that it’s not going to result in totally the same os.stat()
output, regardless.
04:47
Now, if you need to copy a directory, shutil.copy()
will not work. If I try to just copy 'folder_1'
into 'new_folder1'
, then that won’t work because folder_1/
is a directory.
04:59
So for that, you’ll have to use shutil.copytree()
, which will do exactly the correct thing. It will copy 'folder_1'
into 'new_folder_1'
.
05:10
I can take a look at that. There it is, 'folder_1'
, 'new_folder_1'
copied over perfectly. I can list the contents of 'folder_1'
and list the contents of the new
05:23 version, just to make sure that you see that they are, in fact, identical. So, same file, same everything, all copied over, just fine. Just be careful with this because it copies the whole directory tree there.
05:34 Any subdirectories, those will also be copied. Sometimes that might not be exactly what you want. Maybe you want a little more granularity, and if that’s the case, you’ll have to do a little more work and iterate through these directories on your own.
05:45
So now, let’s talk about moving and renaming files because those are really just the same operation. So your options are shutil.move()
and let’s try to move 'test1.txt'
into 'folder_1/test1.txt'
.
06:05
'test1.txt'
is gone from the top-level directory,
06:10
but it now shows up in folder_1/
. So that works just like you might expect it to. The thing that might be difficult, that I sometimes find troublesome with this is you might be tempted to say something like, move
'test2.txt'
into 'folder_two'
.
06:26 And that actually does work like you would expect it to do. It puts this into the folder that you’re looking for, but that’s not entirely clear, what your intent is there.
06:39
Do you want to rename this text file to a blank file that’s just called folder_2
, or do you want to move it into folder_2/
? So I think that this is probably not a super-advisable syntax.
06:51
What I would do is write out this full path as the destination of the move()
, because otherwise, this could become confusing to maintain, just because it might not be totally clear where you’re trying to actually move this thing.
07:04
That’s just something to keep in mind, even though it’s more of—I would say—more of a stylistic quirk. Now, os.rename()
works in almost exactly the same way.
07:13
Let’s move 'backup2.txt'
into 'folder_2/backup2.txt'
. That works just fine. Does the same stuff. And I can do here listdir()
on 'folder_2'
, and just show you that that’s worked.
07:33
So I have 'test2.txt'
now, because I moved it back here. I have backup2.txt
, and I have the three original Python files that were in there.
07:40
So, a lot of stuff there that all works just fine. And both of these functions work on directories as well. If I want to just rename 'folder_1'
to be 'old_folder_1'
, just so that it’s clear what happened there—when I copied it earlier—then I can do that with no issues.
07:59 It doesn’t raise an error like the copying does and this renaming and moving works just fine. So, that’s how you can copy, rename, and move files and directories in Python.
08:10 In the next lesson, I’m going to talk about a new kind of files, which are archive files.
Liam Pulsifer RP Team on Aug. 5, 2020
That’s extremely interesting, @jfoster! Thanks for the addition :)
carl on Aug. 13, 2020
Another approach to disambiguating the destination for shutil.move
(or shutil.copy
) when the destination is an existing directory is to append a trailing slash. e.g.
shutil.move("test1.txt", "folder_1/")
This will move test1.txt
to the directory folder_1
or will fail with a FileNotFoundError
if folder_1
is not an existing directory.
jamesbrown68 on Aug. 19, 2020
I believe the Unix/Linux command ‘mv’ is intended to move files, unless you don’t specify the destination path, in which case the file stays in the same folder and just gets renamed. I can imagine anyone accustomed to that behavior might use shutil.move() in a similar fashion.
The Zen of Python says:
Explicit is better than Implicit.
Liam Pulsifer RP Team on Aug. 23, 2020
Good point @jamesbrown68 – the balance that Python tries to strike between being consistent with its roots in C and Unix while also following its own Zen is always fascinating to watch play out in practice.
Become a Member to join the conversation.
jfoster on Aug. 5, 2020
Note that there is a subtle difference between os.rename and shutil.move. I found that on Centos at least, the first will only rename files within the same filesystem and will error otherwise.
shutil.move will happily move files across filesystems with a copy/delete. It will still perform an os.rename though if the source and target are within the same filesystem so could be a safer bet if you are dealing with multiple mounts.