ChatGPT解决这个技术问题 Extra ChatGPT

How to move a file in Python?

How would I do the equivalent of mv src/* dest/ in Python?

>>> source_files = '/PATH/TO/FOLDER/*'
>>> destination_folder = 'PATH/TO/FOLDER'
>>> # equivalent of $ mv source_files destination_folder
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).
How about os.system("mv file1 file2")?
@TrevorBoydSmith it doesn't raise an exception in newer python versions
@Superbman would you provide a github commit url or github issue url? i'm curious when they fixed the issue.
@TrevorBoydSmith, sry, I'm wrong! Turns out it still raises an exception if you provide only a directory as a destination. I was giving a file name as well, hence it overwrote without raising any exceptions

A
Ann Zen

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")

Note that you must include the file name (file.foo) in both the source and destination arguments. If it is changed, the file will be renamed as well as moved.

Note also that in the first two cases the directory in 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.

As has been noted in comments on other answers, 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.


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."
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.
~ 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.
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.
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.
S
Stephan

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.


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.
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.
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.
N
NOhs

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


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
This works perfectly, but it will fail if you want move the file from one device to another (Invalid cross-device link)
@Amar maybe this is better. Path("path/to/current/file.foo").rename(Path("path/to/new/destination/for") / Path.name))
j
jmontross

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
>>> shutil.move(destination, source)
>>> 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.


docs.python.org/2/library/shutil.html This documentation shows that you have you your parameters switched for the shutil.move method.
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.
error in the example. src,dst is reversed !
i
iggy12345

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)

Now fully functional. Hope this helps you.

Edit:

I've turned 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))

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
J
Javier Palacios

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.


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.
M
Muhammedogz

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.


Why to call external process when python has API for that ?
L
LightCC

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.


I believe this would be system dependent. I don't see mv being used successfully on a windows operating system.
@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.
@LightCC Good catch! Thanks!
I updated the answer with the correct information in the final paragraph rather than a question if it's system dependent.
a
alper

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)

S
Suraj Rao

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

import os
os.system("mv src/* dest/")

K
Kristof U.
  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:


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.
Do you mean file instead of f?