root/livinglogic.python.xist/src/ll/scripts/ucp.py @ 4437:6f3d4e845072

Revision 4437:6f3d4e845072, 7.0 KB (checked in by Walter Doerwald <walter@…>, 8 years ago)

Fix typos in script documentation. Add examples. Bump version number.

Line 
1#!/usr/local/bin/python
2# -*- coding: utf-8 -*-
3
4
5## Copyright 2007-2011 by LivingLogic AG, Bayreuth/Germany.
6## Copyright 2007-2011 by Walter Dörwald
7##
8## All Rights Reserved
9##
10## See ll/__init__.py for the license
11
12
13"""
14Purpose
15-------
16
17``ucp`` is a script that copies files/directory contents. It is an URL-enabled
18version of the ``cp`` system command. Via :mod:`ll.url` and :mod:`ll.orasql`
19``ucp`` supports ``ssh`` and ``oracle`` URLs too.
20
21
22Options
23-------
24
25``ucp`` supports the following options:
26
27    ``urls``
28        Two or more URLs. If more than two URLs are given or the last URL refers
29        to an existing directory, the last URL is the target directory. All other
30        sources are copied into this target directory. Else one file is copied
31        to another file.
32
33    ``-v``, ``--verbose`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
34        Give a report during the copy process about the files copied and their
35        sizes.
36
37    ``-c``, ``--color`` : ``yes``, ``no`` or ``auto``
38        Should the ouput be colored. If ``auto`` is specified (the default) then
39        the output is colored if stdout is a terminal.
40
41    ``-u``, ``--user``
42        A user id or name. If given ``ucp`` will change the owner of the
43        target files.
44
45    ``-g``, ``--group``
46        A group id or name. If given ``ucp`` will change the group of the
47        target files.
48
49    ``-r``, ``--recursive`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
50        Copies files recursively.
51
52    ``-x``, ``--ignoreerrors`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
53        Ignores errors occuring during the copy process. (Otherwise the copy
54        process is aborted.)
55
56    ``-i``, ``--include`` : regular expression
57        Only copy files that contain the regular expression.
58
59    ``-e``, ``--exclude`` : regular expression
60        Don't copy files that contain the regular expression.
61
62    ``-a``, ``--all`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
63        Include dot files (i.e. files whose name starts with a ``.``). Not that
64        the content of directories whose name starts with a dot will still be
65        copied.
66
67
68Examples
69--------
70
71Copy one file to another::
72
73    $ ucp foo.txt bar.txt
74
75Copy a file into an existing directory::
76
77    $ ucp foo.txt dir/
78
79Copy multiple files into a new or existing directory (and give a progress
80report)::
81
82    $ ucp foo.txt bar.txt baz.txt dir/ -v
83    ucp: foo.txt -> dir/foo.txt (1114 bytes)
84    ucp: bar.txt -> dir/bar.txt (2916 bytes)
85    ucp: baz.txt -> dir/baz.txt (35812 bytes)
86
87Recursively copy the schema objects in an Oracle database to a local directory::
88
89    ucp oracle://user:pwd@oracle.example.org/ db/ -r
90
91Recursively copy the schema objects in an Oracle database to a remote directory::
92
93    ucp oracle://user:pwd@oracle.example.org/ ssh://user@www.example.org/~/db/ -r
94"""
95
96
97import sys, re, argparse, contextlib
98
99from ll import misc, url
100
101try:
102    import astyle
103except ImportError:
104    from ll import astyle
105
106try:
107    from ll import orasql # activate the oracle scheme
108except ImportError:
109    pass
110
111
112__docformat__ = "reStructuredText"
113
114
115def main(args=None):
116    def match(url):
117        strurl = str(url)
118        if args.include is not None and args.include.search(strurl) is None:
119            return False
120        if args.exclude is not None and args.exclude.search(strurl) is not None:
121            return False
122        if not args.all:
123            if url.file:
124                name = url.file
125            elif len(url.path) >=2:
126                name = url.path[-2]
127            else:
128                name = ""
129            if name.startswith("."):
130                return False
131        return True
132
133    def copyone(urlread, urlwrite):
134        strurlread = str(urlread)
135        if urlread.isdir():
136            if args.recursive:
137                for u in urlread.listdir():
138                    copyone(urlread/u, urlwrite/u)
139            else:
140                if args.verbose:
141                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), astyle.style_warn(" (directory skipped)"))
142                    stderr.writeln(msg)
143        else:
144            if match(urlread):
145                if args.verbose:
146                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), " -> ")
147                    stderr.write(msg)
148                try:
149                    with contextlib.closing(urlread.open("rb")) as fileread:
150                        with contextlib.closing(urlwrite.open("wb")) as filewrite:
151                            size = 0
152                            while True:
153                                data = fileread.read(262144)
154                                if data:
155                                    filewrite.write(data)
156                                    size += len(data)
157                                else:
158                                    break
159                    if user or group:
160                        urlwrite.chown(user, group)
161                except Exception, exc:
162                    if args.ignoreerrors:
163                        if args.verbose:
164                            excname = exc.__class__.__name__
165                            excmodule = exc.__class__.__module__
166                            if excmodule != "exceptions":
167                                excname = "{}.{}".format(excmodule, excname)
168                            excmsg = str(exc).replace("\n", " ").strip()
169                            msg = astyle.style_error(" (failed with {}: {})".format(excname, excmsg))
170                            stderr.writeln(msg)
171                    else:
172                        raise
173                else:
174                    if args.verbose:
175                        msg = astyle.style_default(astyle.style_url(str(urlwrite)), " (", str(size), " bytes)")
176                        stderr.writeln(msg)
177            else:
178                if args.verbose:
179                    msg = astyle.style_default("ucp: ", astyle.style_url(strurlread), astyle.style_warn(" (skipped)"))
180                    stderr.writeln(msg)
181
182    p = argparse.ArgumentParser(description="Copies URLs")
183    p.add_argument("urls", metavar="url", help="either one source and one target file, or multiple source files and one target dir", nargs="*", type=url.URL)
184    p.add_argument("-v", "--verbose", dest="verbose", help="Be verbose? (default: %(default)s)", action=misc.FlagAction, default=False)
185    p.add_argument("-c", "--color", dest="color", help="Color output (default: %(default)s)", default="auto", choices=("yes", "no", "auto"))
186    p.add_argument("-u", "--user", dest="user", help="user id or name for target files")
187    p.add_argument("-g", "--group", dest="group", help="group id or name for target files")
188    p.add_argument("-r", "--recursive", dest="recursive", help="Copy stuff recursively? (default: %(default)s)", action=misc.FlagAction, default=False)
189    p.add_argument("-x", "--ignoreerrors", dest="ignoreerrors", help="Ignore errors? (default: %(default)s)", action=misc.FlagAction, default=False)
190    p.add_argument("-i", "--include", dest="include", metavar="PATTERN", help="Include only URLs matching PATTERN (default: %(default)s)", type=re.compile)
191    p.add_argument("-e", "--exclude", dest="exclude", metavar="PATTERN", help="Exclude URLs matching PATTERN (default: %(default)s)", type=re.compile)
192    p.add_argument("-a", "--all", dest="all", help="Include dot files? (default: %(default)s)", action=misc.FlagAction, default=False)
193
194    args = p.parse_args(args)
195    if len(args.urls) < 2:
196        p.error("need at least one source url and one target url")
197        return 1
198
199    if args.color == "yes":
200        color = True
201    elif args.color == "no":
202        color = False
203    else:
204        color = None
205    stdout = astyle.Stream(sys.stdout, color)
206    stderr = astyle.Stream(sys.stderr, color)
207
208    user = args.user
209    try:
210        user = int(user)
211    except (TypeError, ValueError):
212        pass
213
214    group = args.group
215    try:
216        group = int(group)
217    except (TypeError, ValueError):
218        pass
219
220    with url.Context():
221        urls = args.urls
222        if len(urls) > 2 or urls[-1].isdir(): # treat target as directory
223            for u in urls[:-1]:
224                copyone(u, urls[-1]/u.file)
225        else:
226            copyone(urls[0], urls[-1])
227
228
229if __name__ == "__main__":
230    sys.exit(main())
Note: See TracBrowser for help on using the browser.