如何修改此rsync命令以找出带有'。'的目录在Python?

时间:2013-07-02 05:18:58

标签: python rsync python-2.6

我正在使用像python这样的rsync命令:

rsync_out = subprocess.Popen(['sshpass', '-p', password, 'rsync', '--recursive', source], 
                     stdout=subprocess.PIPE)
        command = subprocess.Popen(('grep', '\.'), stdin=rsync_out.stdout, stdout=subprocess.PIPE).communicate()[0]

使用grep显示如下文件的目的:

rathi/20090209.02s1.2_sequence.txt

rathi/20090729.02s4.2_sequence.txt.gz

rathi/Homo_sapiens_UCSC_hg19.tar.gz

    rathi/hello/ok.txt

而不是

rathi

rathi/20090209.02s1.2_sequence.txt

rathi/20090729.02s4.2_sequence.txt.gz

rathi/Homo_sapiens_UCSC_hg19.tar.gz

hello

rathi/hello/ok.txt

除非目录名称上有'.',否则它可以正常工作。

如果目录名为hello.v1,则输出为:

rathi/hello.v1
rathi/hello.v1/ok.txt

由于hello.v1是一个目录名,我只想这样显示:

rathi/hello.v1/ok.txt

我该怎么做?

1 个答案:

答案 0 :(得分:1)

我个人不打算使用grep,我只是使用Python自己的字符串过滤 - 但是,这不是你问的问题。

由于文件名是远程的,并且Python将它们视为简单的字符串,因此我们不能使用任何Python自己的文件操作例程(例如os.path.isdir())。所以,我认为你有三种基本方法:

  1. 用斜杠拆分每个字符串,并使用它在内存中构建自己的文件系统树表示。然后,遍历树并仅显示叶节点(即文件)。

  2. 如果您可以假设目录中的文件始终紧跟在该目录之后,那么您可以对以前的条目进行快速检查,以查看此条目是否是其中一个目录中的文件。

  3. 使用rsync中的元信息。

  4. 我会建议第三种选择。我对rsync的体验是,它通常会为您提供完整的文件信息:

    drwxr-xr-x        4096 2013/06/14 17:19:13 tmp/t
    -rwxrwxr-x       14532 2013/06/14 17:17:23 tmp/t/a.out
    -rwxrwxr-x       14539 2013/06/14 17:19:13 tmp/t/static-order
    

    在您的示例中,我看不到任何删除此附加信息的代码,您可以通过查找以d而不是{{1}开头的任何行来轻松使用此代码来过滤目录}。

    如果您没有此扩展信息,则需要执行其他两项中的一项。第一个选项非常简单 - 只需用斜杠拆分然后下降标准树结构,为尚未看到的目录和文件添加条目。解析完所有条目后,您可以遍历树并打印出任何没有子节点的节点。

    第二个选项更复杂,但内存效率更高,您可以在其中维护父目录列表并检查它们是否是列表中当前项的前缀。如果是这样,您可以确定前一个是目录,当前的是一个文件,因此您可以将前一个标记为不显示的内容。如果-以可预测的顺序返回它们,您也可以在退出该目录后将该项目从该列表中删除。您必须确保仅检查斜杠边界处的前缀(因此rsync不是foo/dir的父级,但它是foo/dir-bar的父级)。通常这种方法相当繁琐,除非你正在处理一个非常大的目录树,否则其他一种方法可能更可取。

    顺便说一句,任何一种基于纯字符串的方法也有一个缺点,即空目录与文件无法区分,因为只有目录中存在或不存在区分它们的文件。这是我建议使用foo/dir/bar中的元信息的另一个原因。

    修改

    根据要求,使用rsync元数据的示例:

    rsync

    在此示例中,假设已设置适当的SSH密钥,我将直接调用import subprocess cmdline = ["rsync", "-e", "ssh", "-r", "user@host:/dir"] proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE) for entry in proc.stdout: items = entry.strip().split(None, 4) if not items[0].startswith("d") and "." in items[4]: print items[4] 并使用rsync。我强烈建议使用SSH密钥而不是ssh实用程序 - 从安全角度来看,以纯文本格式存储密码是一个非常糟糕的主意。如果你不担心它们被盗,你可以随时设置没有密码的密钥。有很多页面可以解释如何创建SSH密钥(例如this one)。

    sshpassuserhost替换为您在远程计算机上的用户名,远程计算机的主机名以及您希望在远程计算机上列出的父目录(您可以如果要列出用户的主目录,请省略/dir。否则代码应该未经修改地运行。如果将打印它找到的每个文件的路径名,则跳过不包含点的目录和项。如果你的点过滤器只是试图跳过目录,你可以省略'和'。“在项目[4]'。

    编辑2

    这个例子只是打印条目,但当然你可能想要做其他事情。如果你想要非常聪明,你可以把它写成一个生成器,当它们出现时调用项目上的/dir。我在下面有一个例子,它也打印了这些项目,但你可以看到它可以用来做任何其他事情。我还添加了一些更好的错误处理,以确保yield的使用不会死锁:

    编辑3: 我已更新此示例,还包括文件大小和修改时间。这是基于我从subprocess返回的内容 - 如果您的格式不同,则可能需要使用rsync中的不同成员,或者可能将格式字符串更改为items以匹配您strptime()返回的格式。

    rsync

    如果您愿意,您应该可以将整个from datetime import datetime import os import subprocess def find_remote_files(hostspec): cmdline = ["rsync", "-e", "ssh", "-r", hostspec] with open(os.devnull, "w") as devnull: proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=devnull) try: for entry in proc.stdout: items = entry.strip().split(None, 4) if not items[0].startswith("d"): dt = datetime.strptime(" ".join(items[2:4]), "%Y/%m/%d %H:%M:%S") yield (int(items[1]), dt, items[4]) proc.wait() except: # On any exception, terminate process and re-raise exception. proc.terminate() proc.wait() raise for filesize, filedate, filename in find_remote_files("user@host:/dir"): print "Filename: %s" % (filename,) print "(%d bytes, modified %s)" % (filesize, filedate.strftime("%Y-%m-%d")) 功能粘贴到代码中并直接使用。