从本地Linux文件夹移动到挂载的Windows共享cifs
我需要在一个脚本中把文件从本地的ext4硬盘上的文件夹移动到一个Windows共享的文件夹,这个共享文件夹是通过下面的命令挂载的:mount -t cifs -o username=username,password=password,rw,nounix,iocharset=utf8,file_mode=0777,dir_mode=0777 //192.168.1.120/storage /mnt/storage
我尝试使用了os.rename(src,dst)
、shutil.move(src,dst)
,甚至还用了subprocess.call(['mv', src,dst], Shell=True)
或者subprocess.call(['mv', src,dst])
来移动文件。
但是每次都出现错误,我觉得这可能是因为Linux的文件所有权和权限问题。
比如,当我用mv /mnt/networkshare/file1.txt /tmp/file1.txt
时是没问题的,但
mv /tmp/file1.txt /mnt/networkshare/file1.txt
结果却是
"mv: preserving times for /mnt/networkshare/file1.txt: Operation not permitted"
"mv preserving permissions for /mnt/networkshare/file1.txt: Operation not permitted"
我猜os.rename(src,dst)
和shutil.move(src,dst)
也会遇到同样的问题,不过它们的错误信息不太详细。
shutil.move(src,dst)
给我的错误是:[Errno 1] 操作不允许: '/mnt/networkshare/file1.txt'
而os.rename(src,dst)
则提示:[Errno 18] 无效的跨设备链接
补充一下:pcmanfm可以顺利地从本地剪切并粘贴到远程。
还有让我困惑的是,有些文件是可以移动的……
2 个回答
从Python 3.5版本开始,你可以使用:
shutil.move(src, dst, copy_function=shutil.copyfile)
shutil.copyfile
这个函数只会复制文件的内容,不会保留文件的权限和其他信息。
os.rename
这个函数不能在不同的文件系统之间移动文件,因为它底层的 rename
系统调用 不允许这样做:
rename() 在不同的挂载点之间是无法工作的,即使两个挂载点上挂载的是同一个文件系统。
至于为什么 shutil.move
会失败,答案也在 它的文档 中:
如果目标在当前文件系统上,那么就直接使用 rename。否则,先用 copy2() 将源文件复制到目标位置,然后再删除源文件。
我们来 看看 copy2
是什么吧!
它和 copy() 类似,但还会复制文件的元数据——实际上,这就是先执行 copy() 然后执行 copystat()。
所以,问题出在 copystat
上——因为它无法在这样的挂载点上设置文件的元数据。
由于 shutil
似乎没有一个可以在不复制元数据的情况下重命名的函数,我们得自己动手。让我们看看它的源代码:
In [3]: print inspect.getsource(shutil.move)
def move(src, dst):
"""Recursively move a file or directory to another location. This is
similar to the Unix "mv" command.
If the destination is a directory or a symlink to a directory, the source
is moved inside the directory. The destination path must not already
exist.
If the destination already exists but is not a directory, it may be
overwritten depending on os.rename() semantics.
If the destination is on our current filesystem, then rename() is used.
Otherwise, src is copied to the destination and then removed.
A lot more could be done here... A look at a mv.c shows a lot of
the issues this implementation glosses over.
"""
real_dst = dst
if os.path.isdir(dst):
if _samefile(src, dst):
# We might be on a case insensitive filesystem,
# perform the rename anyway.
os.rename(src, dst)
return
real_dst = os.path.join(dst, _basename(src))
if os.path.exists(real_dst):
raise Error, "Destination path '%s' already exists" % real_dst
try:
os.rename(src, real_dst)
except OSError:
if os.path.isdir(src):
if _destinsrc(src, dst):
raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
copytree(src, real_dst, symlinks=True)
rmtree(src)
else:
copy2(src, real_dst)
os.unlink(src)
看起来,正如我们预期的那样,我们只需要把 copy2
替换成 copy
。我们可以通过复制源代码并重命名这个函数来实现,或者简单地
def move_without_copying_stat(src,dst):
old= shutil.copy2
shutil.copy2= shutil.copy
shutil.move(src,dst)
shutil.copy2= old
如果你今天运气不错的话。理解 这样做的后果 就留给读者自己去思考了。