How do I move a file in Python?
Asked Answered
K

11

1470

How can I do the equivalent of mv in Python?

mv "path/to/current/file.foo" "path/to/new/destination/for/file.foo"
Kevin answered 13/1, 2012 at 22:17 Comment(2)
for those of you familiar with gnu-coreutils' mv command, python's shutil.move has one edge case where shutil.move function differs. Go here for full write up. In a nutshell, Python's shutil.move will raise an exception (but gnu-coreutils mv will not) when your destination is a directory and the directory already has a file with the same name as the source (again for more info see the link provided in the previous sentence).Torchwood
I feel it is extremely unfair to edit a question to match the accepted answer. The question didnt ask to move a file into a file but a file(s) into a directoryThyroid
D
2193

os.rename(), os.replace(), or shutil.move()

All employ the same syntax:

import os
import shutil

os.rename("path/to/current/file.foo", "path/to/new/destination/for/file.foo")
os.replace("path/to/current/file.foo", "path/to/new/destination/for/file.foo")
shutil.move("path/to/current/file.foo", "path/to/new/destination/for/file.foo")
  • The filename ("file.foo") must be included in both the source and destination arguments. If it differs between the two, the file will be renamed as well as moved.
  • The directory within which the new file is being created must already exist.
  • On Windows, a file with that name must not exist or an exception will be raised, but os.replace() will silently replace a file even in that occurrence.
  • shutil.move simply calls os.rename in most cases. However, if the destination is on a different disk than the source, it will instead copy and then delete the source file.
Doridoria answered 13/1, 2012 at 22:19 Comment(11)
Am I the only one that thinks os.rename is not working for directories? I quote: "If dst is a directory, OSError will be raised."Gog
shutil.move works for directories. You can use relative path shutil.move(f.name, "tmp/") or full path shutil.move(f.name, "/Users/hello/tmp/"), do not use ~ in the path, checked in python2.7.9, Mac OS X.Titleholder
~ is a shell construct, and has nothing to do with file paths per se, other than as a misplaced convention. If you really want to involve your home directory, use os.getenv('HOME') instead, concatenating it with parts of your desired path, if necessary.Mckenna
You could always use os.path.expanduser() to properly expand the '~' according to os-specific rules. Much neater since %HOME% isn't always set on Windows.Doridoria
If you want to use os then os.rename behaves differently on different platforms if the file exists, instead use os.replacePosthaste
os.rename won't handle files across different devices. Use shutil.move if you are not sure the source and the destination file are on the same device.Protagoras
On my Mac, os.rename would err with OSError: [Errno 2] No such file or directory, even though it seemed to work fine (except for throwing the error). os.replace is undefined (python 2.7). shutil.move seems to work fineMontoya
@Gog It works with directories, but the destination can not be an existing directory. If destination is an existing file, it will be replaced. Normally destination should not exist, and if it is a directory, it will not replace that whole directory with something else. rename is a simple command with clear semantics. It does not make the assumption to place a file inside a directory if destination is a directory. But you can move whole directories with it.Amendment
I'd like to point out that at least for Windows running Python 3.7.3 that shutil.move respects the file attributes, both created and modified which is really great. This was a folder to folder move on the same disk. It would be really great to run those experiments for each of the commands above and tabulate the attribute preserving behavior across OS platforms and even drives by the looks of it.Lippmann
I do not think this is valid for shutil.move: Note that you must include the file name (file.foo) in both the source and destination arguments.Exergue
@Doridoria When you wrote "Note also that in the first two cases the directory in which the new file is being created must already exist" I read that as implying that this is not true for the third case. That's wrong, as I found out the hard way. The destination directories must already exist for shutil.move().Berzelius
U
341

Although os.rename() and shutil.move() will both rename files, the command that is closest to the Unix mv command is shutil.move(). The difference is that os.rename() doesn't work if the source and destination are on different disks, while shutil.move() is files disk agnostic.

Unimposing answered 30/5, 2013 at 21:12 Comment(3)
shutil.move() uses os.rename() if the destination is on the current filesystem. Otherwise, shutil.move() copies the source to destination using shutil.copy2() and then removes the source.Piranha
Take care to realize that shutil.copy2() can't copy all file metadata, so if that happens it's like doing cp -p and then rm, I gather.Dispute
Be aware: shutil.move in Python 2.7.3 fails if the destination already exists. So if that is possible, either catch the error, or manually remove the file/dir, then do the move.Momism
R
132

After Python 3.4, you can also use pathlib's class Path to move file.

from pathlib import Path

Path("path/to/current/file.foo").rename("path/to/new/destination/for/file.foo")

https://docs.python.org/3.4/library/pathlib.html#pathlib.Path.rename

Roussillon answered 12/10, 2018 at 7:39 Comment(3)
I used it recently in the form of --> Path("path/to/current/file.foo").rename("path/to/new/destination/for/".joinpath(Path.name)) to move all the *.LNK (shortcut) files to a DUMP directory. Worked like a charm! :DPugnacious
This works perfectly, but it will fail if you want move the file from one device to another (Invalid cross-device link)Brindabrindell
@Pugnacious maybe this is better. Path("path/to/current/file.foo").rename(Path("path/to/new/destination/for") / Path.name))Roussillon
B
46

For either the os.rename or shutil.move you will need to import the module. No * character is necessary to get all the files moved.

We have a folder at /opt/awesome called source with one file named awesome.txt.

in /opt/awesome
○ → ls
source
○ → ls source
awesome.txt

python 
>>> source = '/opt/awesome/source'
>>> destination = '/opt/awesome/destination'
>>> import os
>>> os.rename(source, destination)
>>> os.listdir('/opt/awesome')
['destination']

We used os.listdir to see that the folder name in fact changed. Here's the shutil moving the destination back to source.

>>> import shutil
>>> source = '/opt/awesome/destination' 
>>> destination = '/opt/awesome/source'
>>> shutil.move(source, destination)
>>> os.listdir('/opt/awesome/source')
['awesome.txt']

This time I checked inside the source folder to be sure the awesome.txt file I created exists. It is there

Now we have moved a folder and its files from a source to a destination and back again.

Bui answered 22/2, 2013 at 2:30 Comment(3)
docs.python.org/2/library/shutil.html This documentation shows that you have you your parameters switched for the shutil.move method.Mcreynolds
I used the destination and source reversed to see that the files moved from the source and then back to it.... I could see how that is unclear.Bui
error in the example. src,dst is reversed !Vesica
I
31

This is what I'm using at the moment:

import os, shutil
path = "/volume1/Users/Transfer/"
moveto = "/volume1/Users/Drive_Transfer/"
files = os.listdir(path)
files.sort()
for f in files:
    src = path+f
    dst = moveto+f
    shutil.move(src,dst)

You can also turn this into a function, that accepts a source and destination directory, making the destination folder if it doesn't exist, and moves the files. Also allows for filtering of the src files, for example if you only want to move images, then you use the pattern '*.jpg', by default, it moves everything in the directory

import os, shutil, pathlib, fnmatch

def move_dir(src: str, dst: str, pattern: str = '*'):
    if not os.path.isdir(dst):
        pathlib.Path(dst).mkdir(parents=True, exist_ok=True)
    for f in fnmatch.filter(os.listdir(src), pattern):
        shutil.move(os.path.join(src, f), os.path.join(dst, f))
Internment answered 12/11, 2016 at 19:2 Comment(1)
You can easily turn this into a filtered move by using fnmatch.filter(), see my edit. Also, its best to use os.path.join(parent_path, filename) instead of string concatenation to avoid cross-platform issuesHyperostosis
T
19

The accepted answer is not the right one, because the question is not about renaming a file into a file, but moving many files into a directory. shutil.move will do the work, but for this purpose os.rename is useless (as stated on comments) because destination must have an explicit file name.

Thyroid answered 17/10, 2016 at 14:23 Comment(1)
Not useless, simply requires more work to get it to move multiple files. You can get file names with os.path.basename(my_file_path) and the file directories with os.path.dirname(my_file_path). Additionally, it was not made very clear by the OP if he wanted to move multiple files. He mentioned moving only one file in the question, but his example code implied moving multiple files.Supervise
E
4

Since you don't care about the return value, you can do

import os
os.system("mv src/* dest/")
Enrich answered 21/7, 2022 at 5:56 Comment(0)
V
2

Also possible with using subprocess.run() method.

python:
>>> import subprocess
>>> new = "/path/to/destination"
>>> old = "/path/to/new/destination"
>>> process = "mv ..{} ..{}".format(old,new)
>>> subprocess.run(process, shell=True) # do not remember, assign shell value to True.

This will work fine when working on Linux. Windows probably gives error since there is no mv Command.

Variety answered 19/4, 2021 at 18:34 Comment(1)
Why to call external process when python has API for that ?Mango
R
1

Based on the answer described here, using subprocess is another option.

Something like this:

subprocess.call("mv %s %s" % (source_files, destination_folder), shell=True)

I am curious to know the pro's and con's of this method compared to shutil. Since in my case I am already using subprocess for other reasons and it seems to work I am inclined to stick with it.

This is dependent on the shell you are running your script in. The mv command is for most Linux shells (bash, sh, etc.), but would also work in a terminal like Git Bash on Windows. For other terminals you would have to change mv to an alternate command.

Reed answered 16/6, 2018 at 1:50 Comment(4)
I believe this would be system dependent. I don't see mv being used successfully on a windows operating system.Polyphemus
@JoshuaSchlichting It would be more shell dependent than platform type. For example, on Windows this will work fine in a Git Bash terminal, but not using Cmd.Ritzy
@Ritzy Good catch! Thanks!Polyphemus
I updated the answer with the correct information in the final paragraph rather than a question if it's system dependent.Ritzy
T
1

This is solution, which does not enables shell using mv.

from subprocess import Popen, PIPE, STDOUT

source = "path/to/current/file.foo", 
destination = "path/to/new/destination/for/file.foo"

p = Popen(["mv", "-v", source, destination], stdout=PIPE, stderr=STDOUT)
output, _ = p.communicate()
output = output.strip().decode("utf-8")
if p.returncode:
    print(f"E: {output}")
else:
    print(output)
Translation answered 12/9, 2018 at 12:38 Comment(0)
C
-1
  import os,shutil

  current_path = "" ## source path

  new_path = "" ## destination path

  os.chdir(current_path)

  for files in os.listdir():

        os.rename(files, new_path+'{}'.format(f))
        shutil.move(files, new_path+'{}'.format(f)) ## to move files from 

different disk ex. C: --> D:

Chimkent answered 7/5, 2018 at 12:6 Comment(2)
if you are using Python3.# you can use the new f-string intrerpolation: f"{new_path}{f}" but given that you have no static text in your string, this may be more work.... I've been trying to get into the habit of using f-strings though.Gerber
Do you mean file instead of f?Afro

© 2022 - 2024 — McMap. All rights reserved.