How to Copy a File in Python
Introduction
When it comes to using Python to copy files, there are two main ways: using the shutil
module or the os
module. All of the os
methods we show here are methods that allow us to execute shell commands from our Python code, which we’ll use to execute the copy
command (Windows) or the cp
command (Unix).
You’ll notice that many of these methods, in both the shutil
module and the os
module, have very similar functionality (which shouldn’t be surprising), but each varies in functionality from each other very slightly, which I’ll explain as well.
Copying Files with the shutil Module
The shutil module offers several high level methods to copy files. Here below are the main ones:
copyfile
This method copies the content of one file into another file. The destination provided to it must be a writable file, and have a different name than the source file. If the names are the same then it will generate an error. If the destination file already exists, it will be replaced with the newly copied file.
The syntax for this method is:
shutil.copyfile(src_file, dest_file, *, follow_symlinks=True)
For example, the following code will copy a file named “file1.txt” into a file named “file2.txt”:
import shutil
shutil.copyfile('file1.txt', 'file2.txt')
One interesting and potentially useful feature of shutil.copyfile
is the follow_symlinks
Boolean argument. If it is set to False
, and the source file is a symbolic link, then instead of copying the file, a new symbolic link will be created.
copy
This method is very similar to copyfile
, with the main difference being that in addition to copying the content of the source file, it goes one step further and also copies the file’s file system permissions. Copying file permissions is not a trivial task in most programming languages, so this is a nice feature to have.
The syntax is as follows:
shutil.copy(src_file, dest_file, *, follow_symlinks=True)
Each of these parameters are the same as in the copyfile
method. For example, the following code will copy “file1.txt” into “file3.txt”.
import shutil
shutil.copy('file1.txt', 'file3.txt')
Note: Make sure you don’t name your script the same as one of the module you’re importing (which I mistakenly did when testing code for this article). If you do, then you’ll get an error when trying to import that module due to a circular import problem.
copy2
As with the previous methods, copy2
method is identical to the copy
method, but in addition to copying the file contents it also attempts to preserve all the source file’s metadata. If the platform doesn’t allow for full metadata saving, then copy2
doesn’t return failure and it will just preserve any metadata it can.
The syntax is as follows:
shutil.copy2(src_file, dest_file, *, follow_symlinks=True)
Again, these parameters are the same as in the previous commands we’ve mentioned so far.
For example, the following code will copy “file1.txt” into “file4.txt”, as well as preserve the metadata of the original file, “file1.txt”.
import shutil
shutil.copy2('file1.txt', 'file4.txt')
$ python copy-files.py
$ ls -l
total 32
-rw-r--r-- 1 scott staff 91 Oct 27 11:26 copy-files.py
-rw-r--r-- 1 scott staff 6 Oct 27 11:27 file1.txt
-rw-r--r-- 1 scott staff 6 Oct 27 11:29 file3.txt
-rw-r--r-- 1 scott staff 6 Oct 27 11:27 file4.txt
As we can see from executing our code above, “file1.txt” was copied to “file4.txt”. However, you may have noticed the creation date was preserved on the new file, unlike with shutil.copy
, which copied “file1.txt” to “file3.txt” and gave it a new creation date.
copyfileobj
This method copies the content of a source file into a destination file, from the current source-file position. What this means is that if you read data from your source file object, then the position you stop reading at is the position copyfileobj
starts copying from.
The syntax is as follows:
shutil.copyfileobj(src_file_object, dest_file_object[, length])
The meanings of source and destination file parameters are similar to the previous commands, but now, they refer to objects. The length parameter is optional and represents the buffer size that is the number of bites kept in memory during the copy process. This option can be useful when copying very large files, as it can speed up the copying process and avoids uncontrolled memory usage.
For example the following code will copy “file1.txt” into “file5.txt”
import shutil
filename1 = 'file1.txt'
fileA = open(filename1, 'rb')
filename2 = 'file5.txt'
fileB = open(filename2, 'wb')
shutil.copyfileobj(fileA, fileB)
As we can see, in order to use copyfileobj
, we need to open the files in binary mode (which is the “b” part of “rb” and “wb”). In addition, the source file must be opened as readable and the destination file must be opened as writable (the “r” and “w” parts, respectively).
Copying Files with the os Module
The os module provides a way to use the operating system functionality to copy your files. In most (if not all) of the examples from here on out we provide examples that work for both Windows and Unix. The examples are different because of the shell commands used, so be sure to pay attention to how each function call is labeled in the Python comments.
popen
This method opens a pipe to or from your command. However, note that this method was deprecated in Python 2.6, so we don’t recommend using it unless you have to. As an alternative, the Python documentation advises us to use methods from the subprocess module instead.
The syntax is as follows:
os.popen(cmd[, mode[, bufsize]])
Here the value returned is a file object that is connected to the pipe. This object can be read from or written to depending on the mode. The default mode is ‘r’, which allows reading of the file contents.
The example below will copy “file1.txt” into “file6.txt”:
import os
# Windows
os.popen('copy file1.txt file6.txt')
# Unix
os.popen('cp file1.txt file6.txt')
Running the command in this way is exactly the same as if you ran it directly from the command line of your terminal.
system
This method executes the specified command in a subshell. It is available for both Unix and Windows. The syntax is as follows:
os.system(command)
Here command
is a string containing the DOS or Unix shell command. In our case, this is where we’ll put the copy
or cp
command.
For example, the following code will copy “file1.txt” into “file7.txt”
import os
# Windows
os.system('copy file1.txt file7.txt')
# Unix
os.system('cp file1.txt file7.txt')
This looks identical to the previous os.popen
command we just used, but the command is executed in a subshell, which means it is executed in a separate thread in parallel to your executing code. To wait for its completion, you need to call .wait()
on the object returned by os.system
.
Copying Files with the subprocess Module
The subprocess module intends to replace some methods in the os
module (particularly os.system
and the os.spawn*
methods), and it presents two main methods to access the operating system commands. These methods are call
and check_output
. Once again, for Unix systems, the command “copy file1.txt file2.txt” should be replaced by “cp file1.txt file2.txt”.
call Method
The Python documentation recommends us to use the call
method to launch a command from the operating system.
The syntax is as follows:
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
The args
parameter will include our shell command. However, a word of caution, as the Python documentation warns us that using shell=True
can be a security risk.
Using this function call, we can run our copy command as follows:
import subprocess
# Windows
status = subprocess.call('copy file1.txt file8.txt', shell=True)
# Unix
status = subprocess.call('cp file1.txt file8.txt', shell=True)
As the example above shows, we simply need to pass a string with the shell command, as before.
And as expected, the operating system will copy “file1.txt” into a file named “file8.txt”.
check_output Method
This method also allows us to execute a command within a shell. It is very much like the subprocess.run
command, except that by default it pipes data from stdout as encoded bytes. The syntax is as follows:
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
Here the args
parameter includes the shell command we want to use. Once again, the Python documentation warns us of using shell=True
, so use this method with caution.
In the following code we’ll copy “file1.txt” to “file9.txt” using the check_output
command:
import subprocess
# Windows
status = subprocess.check_output('copy file1.txt file9.txt', shell=True)
# Unix
status = subprocess.check_output('cp file1.txt file9.txt', shell=True)
And as with all of the commands we’ve shown in this article, this will copy the file “file1.txt” to the destination we specified, which is “file9.txt” here.
Wrapping Up
Python offers us many different ways to copy files, some of which are part of the Python set of methods. Others use some of Python’s powerfull methods to execute commands in a shell, which utilize shell commands like copy
or cp
.
Not sure which one is right for you? We presented a lot of different ways to copy files here, so that’s understandable. The method you use to copy a file is completely up to you and will depend on your specific needs. Although in most cases one of the shutil
commands will work just fine for you. Try starting with shutil.copy2
and see if that does what you need.
Which method do you use and why? Let us know in the comments!