| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2011 Google Inc. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 from gslib.command import Command |
| 16 from gslib.command import COMMAND_NAME |
| 17 from gslib.command import COMMAND_NAME_ALIASES |
| 18 from gslib.command import CONFIG_REQUIRED |
| 19 from gslib.command import FILE_URIS_OK |
| 20 from gslib.command import MAX_ARGS |
| 21 from gslib.command import MIN_ARGS |
| 22 from gslib.command import PROVIDER_URIS_OK |
| 23 from gslib.command import SUPPORTED_SUB_ARGS |
| 24 from gslib.command import URIS_START_ARG |
| 25 from gslib.exception import CommandException |
| 26 from gslib.help_provider import HELP_NAME |
| 27 from gslib.help_provider import HELP_NAME_ALIASES |
| 28 from gslib.help_provider import HELP_ONE_LINE_SUMMARY |
| 29 from gslib.help_provider import HELP_TEXT |
| 30 from gslib.help_provider import HelpType |
| 31 from gslib.help_provider import HELP_TYPE |
| 32 from gslib.util import NO_MAX |
| 33 from gslib.wildcard_iterator import ContainsWildcard |
| 34 |
| 35 _detailed_help_text = (""" |
| 36 <B>SYNOPSIS</B> |
| 37 gsutil mv [-p] src_uri dst_uri |
| 38 - or - |
| 39 gsutil mv [-p] uri... dst_uri |
| 40 |
| 41 |
| 42 <B>DESCRIPTION</B> |
| 43 The gsutil mv command allows you to move data between your local file |
| 44 system and the cloud, move data within the cloud, and move data between |
| 45 cloud storage providers. For example, to move all objects from a |
| 46 bucket to a local directory you could use: |
| 47 |
| 48 gsutil mv gs://my_bucket dir |
| 49 |
| 50 The mv command, like the rm command, will refuse to remove data from |
| 51 the local disk. Thus, for example, this command will not be allowed: |
| 52 |
| 53 gsutil mv *.txt gs://my_bucket |
| 54 |
| 55 |
| 56 <B>RENAMING BUCKET SUBDIRECTORIES</B> |
| 57 You can use the gsutil mv command to rename subdirectories. For example, |
| 58 the command: |
| 59 |
| 60 gsutil mv gs://my_bucket/olddir gs://my_bucket/newdir |
| 61 |
| 62 would rename all objects and subdirectories under gs://my_bucket/olddir to be |
| 63 under gs://my_bucket/newdir, otherwise preserving the subdirectory structure. |
| 64 |
| 65 If you do a rename as specified above and you want to preserve ACLs, you |
| 66 should use the -p option (see OPTIONS). |
| 67 |
| 68 Note that when using mv to rename bucket subdirectories you cannot specify |
| 69 the source URI using wildcards. You need to spell out the complete name: |
| 70 |
| 71 gsutil mv gs://my_bucket/olddir gs://my_bucket/newdir |
| 72 |
| 73 If you have a large number of files to move you might want to use the |
| 74 gsutil -m option, to perform a multi-threaded/multi-processing move: |
| 75 |
| 76 gsutil -m mv gs://my_bucket/olddir gs://my_bucket/newdir |
| 77 |
| 78 |
| 79 <B>OPTIONS</B> |
| 80 -p Causes ACL to be preserved when moving in the cloud. Note that |
| 81 this option has performance and cost implications, because it |
| 82 is essentially performing three requests (getacl, cp, setacl). |
| 83 (The performance issue can be mitigated to some degree by |
| 84 using gsutil -m cp to cause multi-threaded/multi-processing |
| 85 copying.) |
| 86 """) |
| 87 |
| 88 |
| 89 class MvCommand(Command): |
| 90 """Implementation of gsutil mv command. |
| 91 Note that there is no atomic rename operation - this command is simply |
| 92 a shorthand for 'cp' followed by 'rm'. |
| 93 """ |
| 94 |
| 95 # Command specification (processed by parent class). |
| 96 command_spec = { |
| 97 # Name of command. |
| 98 COMMAND_NAME : 'mv', |
| 99 # List of command name aliases. |
| 100 COMMAND_NAME_ALIASES : ['move', 'ren', 'rename'], |
| 101 # Min number of args required by this command. |
| 102 MIN_ARGS : 2, |
| 103 # Max number of args required by this command, or NO_MAX. |
| 104 MAX_ARGS : NO_MAX, |
| 105 # Getopt-style string specifying acceptable sub args. |
| 106 SUPPORTED_SUB_ARGS : 'p', |
| 107 # True if file URIs acceptable for this command. |
| 108 FILE_URIS_OK : True, |
| 109 # True if provider-only URIs acceptable for this command. |
| 110 PROVIDER_URIS_OK : False, |
| 111 # Index in args of first URI arg. |
| 112 URIS_START_ARG : 0, |
| 113 # True if must configure gsutil before running command. |
| 114 CONFIG_REQUIRED : True, |
| 115 } |
| 116 help_spec = { |
| 117 # Name of command or auxiliary help info for which this help applies. |
| 118 HELP_NAME : 'mv', |
| 119 # List of help name aliases. |
| 120 HELP_NAME_ALIASES : ['move', 'rename'], |
| 121 # Type of help: |
| 122 HELP_TYPE : HelpType.COMMAND_HELP, |
| 123 # One line summary of this help. |
| 124 HELP_ONE_LINE_SUMMARY : 'Move/rename objects and/or subdirectories', |
| 125 # The full help text. |
| 126 HELP_TEXT : _detailed_help_text, |
| 127 } |
| 128 |
| 129 # Command entry point. |
| 130 def RunCommand(self): |
| 131 # Check each source arg up, refusing to delete a bucket or directory src |
| 132 # URI (force users to explicitly do that as a separate operation). |
| 133 for arg_to_check in self.args[0:-1]: |
| 134 if self.suri_builder.StorageUri(arg_to_check).names_container(): |
| 135 raise CommandException('Will not remove source buckets or directories ' |
| 136 '(%s).\nYou must separately copy and remove for ' |
| 137 'that purpose.' % arg_to_check) |
| 138 |
| 139 # Expand wildcards, dirs, buckets, and bucket subdirs in StorageUris |
| 140 # before running cp and rm commands, to prevent the |
| 141 # following problem: starting with a bucket containing only the object |
| 142 # gs://bucket/obj, say the user does: |
| 143 # gsutil mv gs://bucket/* gs://bucket/d.txt |
| 144 # If we didn't expand the wildcard first, the cp command would |
| 145 # first copy gs://bucket/obj to gs://bucket/d.txt, and the |
| 146 # rm command would then remove that object. |
| 147 # Note 1: This is somewhat inefficient, since we request a bucket listing |
| 148 # here and then again in the generated cp command. TODO: Consider adding |
| 149 # an internal interface to cp command to allow this expansion to be passed |
| 150 # in. |
| 151 # Note 2: We use recursion_requested when expanding wildcards and containers |
| 152 # so we can determine if any of the source URIs are directories (and then |
| 153 # use cp -R and rm -R to perform the move, to match the behavior of UNIX mv |
| 154 # (where moving a directory moves all the contained files). |
| 155 src_uri_expansion = self.exp_handler.ExpandWildcardsAndContainers( |
| 156 self.args[0:len(self.args)-1], True) |
| 157 exp_arg_list = list(src_uri_expansion.IterExpandedUriStrings()) |
| 158 |
| 159 # Check whether exp_arg_list has any file:// URIs, and disallow it. Note |
| 160 # that we can't simply set FILE_URIS_OK to False in command_spec because |
| 161 # we *do* allow a file URI for the dest URI. (We allow users to move data |
| 162 # out of the cloud to the local disk, but we disallow commands that would |
| 163 # delete data off the local disk, and instead require the user to delete |
| 164 # data separately, using local commands/tools.) |
| 165 if self.HaveFileUris(exp_arg_list): |
| 166 raise CommandException('"mv" command does not support "file://" URIs for ' |
| 167 'source arguments.\nDid you mean to use a ' |
| 168 'gs:// URI?') |
| 169 |
| 170 if src_uri_expansion.IsEmpty(): |
| 171 raise CommandException('No URIs matched') |
| 172 |
| 173 # If any of the src URIs are directories add -R to options to be passed to |
| 174 # cp and rm commands. |
| 175 self.recursion_requested = False |
| 176 for src_uri in src_uri_expansion.GetSrcUris(): |
| 177 if src_uri_expansion.NamesContainer(src_uri): |
| 178 self.recursion_requested = True |
| 179 # Disallow wildcard src URIs when moving directories, as supporting it |
| 180 # would make the name transformation too complex and would also be |
| 181 # dangerous (e.g., someone could accidentally move many objects to the |
| 182 # wrong name, or accidentally overwrite many objects). |
| 183 if ContainsWildcard(src_uri): |
| 184 raise CommandException( |
| 185 'mv command disallows naming source directories using wildcards') |
| 186 |
| 187 # Add command-line opts back in front of args so they'll be picked up by cp |
| 188 # and rm commands (e.g., for -p option). Use undocumented (internal |
| 189 # use-only) cp -M option to request move naming semantics (see |
| 190 # _ConstructDstUri in cp.py). |
| 191 unparsed_args = ['-M'] |
| 192 if self.recursion_requested: |
| 193 unparsed_args.append('-R') |
| 194 exp_arg_list.insert(0, '-R') |
| 195 unparsed_args.extend(self.unparsed_args) |
| 196 self.command_runner.RunNamedCommand('cp', unparsed_args, self.headers, |
| 197 self.debug, self.parallel_operations) |
| 198 # See comment above about why we're passing exp_arg_list instead of |
| 199 # unparsed_args here. |
| 200 self.command_runner.RunNamedCommand('rm', exp_arg_list, |
| 201 self.headers, self.debug, |
| 202 self.parallel_operations) |
| 203 |
| 204 # test specification, see definition of test_steps in base class for |
| 205 # details on how to populate these fields |
| 206 test_steps = [ |
| 207 # (test name, cmd line, ret code, (result_file, expect_file)) |
| 208 ('gen expect files', 'echo 0 >$F0; echo 1 >$F1; echo 2 >$F2', 0, None), |
| 209 ('verify 2 src objs', 'gsutil ls gs://$B2 | wc -l >$F9', 0, ('$F9', '$F2')), |
| 210 ('verify 0 dst objs', 'gsutil ls gs://$B0 | wc -l >$F9', 0, ('$F9', '$F0')), |
| 211 ('mv 2 objects', |
| 212 'gsutil -m mv gs://$B2/$O0 gs://$B2/$O1 gs://$B0 2>&1 | grep Removing', |
| 213 0, None), |
| 214 ('verify 0 src objs', 'gsutil ls gs://$B2 | wc -l >$F9', 0, ('$F9', '$F0')), |
| 215 ('verify 2 dst objs', 'gsutil ls gs://$B0 | wc -l >$F9', 0, ('$F9', '$F2')), |
| 216 ('rm 1 src object', 'gsutil rm gs://$B0/$O0', 0, None), |
| 217 ('verify 1 src obj', 'gsutil ls gs://$B0 | wc -l >$F9', 0, ('$F9', '$F1')), |
| 218 ('verify 0 dst objs', 'gsutil ls gs://$B2 | wc -l >$F9', 0, ('$F9', '$F0')), |
| 219 ('mv 2 objects', |
| 220 'gsutil -m mv gs://$B0/$O0 gs://$B0/$O1 gs://$B2 2>&1 | grep Removing', |
| 221 1, None), |
| 222 ] |
| 223 |
| OLD | NEW |