从本地Linux文件夹移动到挂载的Windows共享cifs

3 投票
2 回答
3084 浏览
提问于 2025-04-30 15:01

我需要在一个脚本中把文件从本地的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 个回答

1

从Python 3.5版本开始,你可以使用:

shutil.move(src, dst, copy_function=shutil.copyfile)

shutil.copyfile 这个函数只会复制文件的内容,不会保留文件的权限和其他信息。

4

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

如果你今天运气不错的话。理解 这样做的后果 就留给读者自己去思考了。

撰写回答