OLD | NEW |
---|---|
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """A wrapper for subprocess to make calling shell commands easier.""" | 5 """A wrapper for subprocess to make calling shell commands easier.""" |
6 | 6 |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import pipes | 9 import pipes |
10 import select | 10 import select |
11 import signal | 11 import signal |
12 import string | |
12 import StringIO | 13 import StringIO |
13 import subprocess | 14 import subprocess |
14 import time | 15 import time |
15 | 16 |
16 # fcntl is not available on Windows. | 17 # fcntl is not available on Windows. |
17 try: | 18 try: |
18 import fcntl | 19 import fcntl |
19 except ImportError: | 20 except ImportError: |
20 fcntl = None | 21 fcntl = None |
21 | 22 |
23 _SafeShellChars = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./') | |
24 | |
25 quote = pipes.quote | |
jbudorick
2014/10/17 09:04:52
I don't think I like this. Let callers use pipes.q
| |
26 | |
27 def dquote(s): | |
jbudorick
2014/10/17 09:04:53
While I understand that you're mimicking pipes.quo
perezju
2014/10/17 11:17:09
Done.
| |
28 """Return an escaped version of the string using double quotes. | |
29 | |
30 A sibling to pipes.quote that uses double rather than single quotes. | |
31 Meant to be used to quote strings which may contain unsafe characters (e.g. | |
32 spaces), while retaining some shell features such as variable interpolation. | |
33 | |
34 The set of characters that retain their special meaning may depend on the | |
35 shell implementation. It usually includes: '$', '`', '\', '!', '*', and '@'. | |
36 """ | |
37 if not s: | |
jbudorick
2014/10/17 09:04:52
Also, this function should have some unit tests.
perezju
2014/10/17 11:17:09
Done.
| |
38 return '""' | |
39 if all(c in _SafeShellChars for c in s): | |
40 return s | |
41 else: | |
42 return '"' + s.replace('"', '\\"') + '"' | |
43 | |
22 | 44 |
23 def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None): | 45 def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None): |
24 return subprocess.Popen( | 46 return subprocess.Popen( |
25 args=args, cwd=cwd, stdout=stdout, stderr=stderr, | 47 args=args, cwd=cwd, stdout=stdout, stderr=stderr, |
26 shell=shell, close_fds=True, env=env, | 48 shell=shell, close_fds=True, env=env, |
27 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) | 49 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) |
28 | 50 |
29 | 51 |
30 def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None): | 52 def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None): |
31 pipe = Popen(args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, | 53 pipe = Popen(args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
81 Returns: | 103 Returns: |
82 The 2-tuple (exit code, output). | 104 The 2-tuple (exit code, output). |
83 """ | 105 """ |
84 if isinstance(args, basestring): | 106 if isinstance(args, basestring): |
85 args_repr = args | 107 args_repr = args |
86 if not shell: | 108 if not shell: |
87 raise Exception('string args must be run with shell=True') | 109 raise Exception('string args must be run with shell=True') |
88 elif shell: | 110 elif shell: |
89 raise Exception('array args must be run with shell=False') | 111 raise Exception('array args must be run with shell=False') |
90 else: | 112 else: |
91 args_repr = ' '.join(map(pipes.quote, args)) | 113 args_repr = ' '.join(map(quote, args)) |
92 | 114 |
93 s = '[host]' | 115 s = '[host]' |
94 if cwd: | 116 if cwd: |
95 s += ':' + cwd | 117 s += ':' + cwd |
96 s += '> ' + args_repr | 118 s += '> ' + args_repr |
97 logging.info(s) | 119 logging.info(s) |
98 pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 120 pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
99 shell=shell, cwd=cwd) | 121 shell=shell, cwd=cwd) |
100 stdout, stderr = pipe.communicate() | 122 stdout, stderr = pipe.communicate() |
101 | 123 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
158 break | 180 break |
159 finally: | 181 finally: |
160 try: | 182 try: |
161 # Make sure the process doesn't stick around if we fail with an | 183 # Make sure the process doesn't stick around if we fail with an |
162 # exception. | 184 # exception. |
163 process.kill() | 185 process.kill() |
164 except OSError: | 186 except OSError: |
165 pass | 187 pass |
166 process.wait() | 188 process.wait() |
167 return process.returncode, output.getvalue() | 189 return process.returncode, output.getvalue() |
OLD | NEW |