1433

How can I do the equivalent of mv in Python?

mv "path/to/current/file.foo" "path/to/new/destination/for/file.foo"
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
David542
  • 104,438
  • 178
  • 489
  • 842
  • 8
    for those of you familiar with [gnu-coreutils' `mv` command](https://www.gnu.org/software/coreutils/manual/html_node/mv-invocation.html), python's `shutil.move` has one edge case where `shutil.move` function differs. [Go here for full write up](https://stackoverflow.com/questions/31813504). **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). – Trevor Boyd Smith Aug 11 '17 at 12:22
  • 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 directory – Javier Palacios Aug 08 '23 at 19:24

11 Answers11

2135

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.
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
ig0774
  • 39,669
  • 3
  • 55
  • 57
  • 10
    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." – Fabian Jun 23 '14 at 20:11
  • 63
    `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. – whyisyoung Apr 21 '15 at 02:26
  • 7
    `~` 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. – Armen Michaeli Jul 24 '15 at 13:11
  • 23
    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. – ig0774 Jul 24 '15 at 15:27
  • If you want to use `os` then `os.rename` behaves differently on different platforms if the file exists, instead use `os.replace` – c z Jan 26 '17 at 11:53
  • 26
    `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. – Giuseppe Scrivano Mar 06 '17 at 11:44
  • 1
    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 fine – Zachary Ryan Smith Sep 07 '18 at 19:26
  • 1
    @Fabian 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. – Bachsau Apr 07 '19 at 12:46
  • 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. – jxramos Feb 15 '20 at 05:53
  • 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.` – Timo Nov 12 '20 at 10:54
  • @ig0774 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()`. – Bob Kline Jun 30 '22 at 12:32
336

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.

Stephan
  • 41,764
  • 65
  • 238
  • 329
Jim Calfas
  • 3,361
  • 1
  • 12
  • 2
  • 107
    `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. – fmalina Jan 21 '14 at 20:01
  • 12
    Take care to realize that [`shutil.copy2()` can't copy all file metadata](https://docs.python.org/2/library/shutil.html), so if that happens it's like doing `cp -p` and then `rm`, I gather. – 2rs2ts Apr 01 '14 at 22:01
  • 11
    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. – Dana May 29 '14 at 17:44
129

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

NOhs
  • 2,780
  • 3
  • 25
  • 59
MoonFruit
  • 1,490
  • 1
  • 11
  • 11
  • 1
    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! :D – Amar Aug 21 '20 at 00:30
  • 10
    This works perfectly, but it will fail if you want move the file from one device to another (Invalid cross-device link) – capooti Sep 22 '20 at 18:50
  • 1
    @Amar maybe this is better. `Path("path/to/current/file.foo").rename(Path("path/to/new/destination/for") / Path.name))` – MoonFruit Apr 16 '21 at 01:56
45

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.

Neuron
  • 5,141
  • 5
  • 38
  • 59
jmontross
  • 3,533
  • 1
  • 21
  • 17
  • 6
    http://docs.python.org/2/library/shutil.html This documentation shows that you have you your parameters switched for the shutil.move method. – mac10688 May 04 '13 at 02:43
  • 3
    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. – jmontross May 07 '13 at 21:07
  • error in the example. src,dst is reversed ! – Ludo Schmidt Mar 30 '22 at 13:17
30

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))
Neuron
  • 5,141
  • 5
  • 38
  • 59
Peter Vlaar
  • 445
  • 4
  • 6
  • 3
    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 issues – iggy12345 Mar 20 '19 at 21:13
17

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.

Javier Palacios
  • 340
  • 3
  • 5
  • 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. – Jacques Mathieu Mar 15 '19 at 22:20
4

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

import os
os.system("mv src/* dest/")
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Diego
  • 867
  • 7
  • 13
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.

Muhammedogz
  • 774
  • 8
  • 21
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.

LightCC
  • 9,804
  • 5
  • 52
  • 92
Bill
  • 10,323
  • 10
  • 62
  • 85
0

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)
alper
  • 2,919
  • 9
  • 53
  • 102
-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:

Kristof U.
  • 1,263
  • 10
  • 17
Ned Slark
  • 11
  • 1
  • 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. – jusopi Aug 22 '18 at 14:29
  • 1
    Do you mean file instead of f? – Matthew Dec 10 '19 at 15:25